这是一个错误,因为您应该始终能够使用 call 对象的 repr 输出来重新创建具有相同值的新 call 对象。
这里的问题是call,unittest.mock._Call 的一个实例,依赖于__getattr__ 方法来实现它的链式调用注解魔法,当一个不存在的属性名是返回另一个_Call 对象时给定的。但是由于_Call 是tuple 的子类,它确实定义了__getitem__ 属性,所以当要求__getitem__ 属性时,_Call.__getattr__ 方法将简单地返回tuple.__getitem__ 而不是_Call 对象。由于tuple.__getitem__ 不接受字符串作为参数,因此您会收到上述错误。
为了解决这个问题,由于确定是否定义了属性是通过调用__getattribute__ 方法完成的,当找不到给定的属性名称时会引发AttributeError,我们可以覆盖_Call.__getattribute__这样当给定的属性名称为'__getitem__' 时,它会引发这样的异常,以有效地使__getitem__“不存在”并将其解析传递给__getattr__ 方法,然后该方法将返回一个_Call 对象就像它对任何其他不存在的属性一样:
def __getattribute__(self, attr):
if attr == '__getitem__':
raise AttributeError
return tuple.__getattribute__(self, attr)
call.__class__.__getattribute__ = __getattribute__ # call.__class__ is _Call
这样:
mm = MagicMock()
mm().foo()['bar']
mm.assert_has_calls([call(), call().foo(), call().foo().__getitem__('bar')])
不会引发异常,而:
mm.assert_has_calls([call(), call().foo(), call().foo().__getitem__('foo')])
会提高:
AssertionError: Calls not found.
Expected: [call(), call().foo(), call().foo().__getitem__('foo')]
Actual: [call(), call().foo(), call().foo().__getitem__('bar')]
演示:https://repl.it/repls/StrikingRedundantAngle
请注意,我已在 Python 错误跟踪器中提交了 bug 并将我的修复作为 pull request 提交给 CPython,因此希望您在不久的将来不再需要执行上述操作。