访问注入对象的包含对象?

时间:2010-09-17 15:16:48

标签: python code-injection

给定一个包含可调用对象B的对象A,有没有办法从B.__call__()内部确定“A”?

这适用于测试注入,其中A.B最初是一种方法。

代码是这样的:

# Class to be tested.
class Outer(object):
  def __init__(self, arg):
    self.arg = arg
  def func(self, *args, **kwargs):
    print ('Outer self: %r' % (self,))

# Framework-provided:
class Injected(object):
  def __init__(self):
    self._replay = False
    self._calls = []
  def replay(self):
    self._replay = True
    self._calls.reverse()
  def __call__(self, *args, **kwargs):
    if not self._replay:
      expected = (args[0], args[1:], kwargs)
      self._calls.append(expected)
    else:
      expected_s, expected_a, expected_k = self._calls.pop()
      ok = True
      # verify args, similar for expected_k == kwargs
      ok = reduce(lambda x, comp: x and comp[0](comp[1]),
                  zip(expected_a, args),
                  ok)
      # what to do for expected_s == self?
      # ok = ok and expected_s(this) ### <= need "this". ###
      if not ok:
        raise Exception('Expectations not matched.')

# Inject:
setattr(Outer, 'func', Injected())
# Set expectations:
# - One object, initialised with "3", must pass "here" as 2nd arg.
# - One object, initialised with "4", must pass "whatever" as 1st arg.
Outer.func(lambda x: x.arg == 3, lambda x: True, lambda x: x=='here')
Outer.func(lambda x: x.arg == 4, lambda x: x=='whatever', lambda x: True)
Outer.func.replay()
# Invoke other code, which ends up doing:
o = Outer(3)
p = Outer(5)
o.func('something', 'here')
p.func('whatever', 'next')  # <- This should fail, 5 != 4

问题是:在Injected.__call__()内是否有办法(黑魔法可以)访问非自覆Outer.func()中的“自我” ,用作“this”(标有“###”的行)?

当然,代码有点复杂(可以将调用配置为任意顺序,可以设置返回值等),但这是我能想出的最小例子,证明了这两个问题和大多数约束。

我无法使用默认参数而不是Outer.func注入一个函数 - 它会中断记录(如果注入了一个函数,它将是一个未绑定的方法,并且需要一个“Outer”实例作为其第一个参数,而不是比较器/验证器)。

理论上,我可以完全为其来电者嘲笑“外面”。然而,设置期望可能会有更多代码,而不是其他地方可重复使用 - 任何类似的案例/项目也必须重新实现“外部”(或其等价物)作为模拟。

2 个答案:

答案 0 :(得分:2)

  

给定一个对象A,其中包含一个   可调用对象B,有没有办法   从内部确定“A”   B.的呼叫()?

不是一般情况下,即在本文中您需要无限的通用性 - 除非B以某种方式保留对A的引用,否则Python肯定不会保留它B代表。

对于您的预期用例,情况更糟:即使B本身 保持对A的引用,它也无济于事(作为方法对象会它的类),因为你在B没有追索setattr的情况下践踏Outer,这相当于一项任务。在课程func中没有任何痕迹,在更快乐的时候,它有setattr属性具有某些特征:该属性不再被{{1}}删除。

这不是真正的依赖注入(一种需要从注入对象进行合作的设计模式),它是猴子修补,我的一个宠儿小便,以及它的破坏性,不可恢复的性质(你现在正在努力)是为什么我对此感到愤怒的一部分。

答案 1 :(得分:0)

也许你想要the Python descriptor protocol