Python 'del' 如果在 Python 3.6 上模拟调用时引发 AttributeError,但在 >=3.7 上不会
Python 'del' raises AttributeError if called on mock on Python 3.6, but not on >=3.7
我正在使用 unittest.mock
来测试我的代码。如果对象不存在,我创建的函数会使用 hasattr
.
向该对象添加一个属性
根据 Python 3.6 and newer versions 中的文档,我可以使用 del
删除 mock 的属性,但它在 Python 3.6 上失败了。在 Python 3.7 及更高版本上,它有效。
这是一个简短的测试,展示了我要测试的内容:
from unittest.mock import Mock
def add_attribute(info):
if not hasattr(info, 'attribute'):
info.attribute = 'Attribute Added'
def test_mock():
info = Mock()
assert hasattr(info, 'attribute')
del info.attribute
assert not hasattr(info, 'attribute')
add_attribute(info)
assert info.attribute == 'Attribute Added'
del info.attribute
assert not hasattr(info, 'attribute')
在 python 3.8 上有效:
======= test session starts ========
platform linux -- Python 3.8.5, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /code
plugins: cov-2.12.1
collected 1 item
test.py .
在 python 3.6 上,它失败了:
============================= test session starts ==============================
platform linux -- Python 3.6.14, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /code
plugins: cov-2.12.1
collected 1 item
test.py F [100%]
=================================== FAILURES ===================================
__________________________________ test_mock ___________________________________
def test_mock():
info = Mock()
assert hasattr(info, 'attribute')
del info.attribute
assert not hasattr(info, 'attribute')
add_attribute(info)
assert info.attribute == 'Attribute Added'
> del info.attribute
test.py:14:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <Mock id='140171347130128'>, name = 'attribute'
def __delattr__(self, name):
if name in _all_magics and name in type(self).__dict__:
delattr(type(self), name)
if name not in self.__dict__:
# for magic methods that are still MagicProxy objects and
# not set on the instance itself
return
if name in self.__dict__:
object.__delattr__(self, name)
obj = self._mock_children.get(name, _missing)
if obj is _deleted:
> raise AttributeError(name)
E AttributeError: attribute
/usr/local/lib/python3.6/unittest/mock.py:728: AttributeError
=========================== short test summary info ============================
FAILED test.py::test_mock - AttributeError: attribute
============================== 1 failed in 0.11s ===============================
test_python exited with code 1
我不确定 unittest 库是否已更新,或者它可能是关于如何在 Python 版本之间传递函数的(非)预期行为。
如有任何见解,我们将不胜感激。
这已在 222d303 for issue bpo-20239
中修复
这已在 d358a8c which first appeared in python 3.7.3rc1
中移植到 3.7
此补丁对 mocks
上的 __delattr__
方法进行了重大更改
~<3.7.3代码摘录:
def __delattr__(self, name):
# ...
if name in self.__dict__:
object.__delattr__(self, name)
obj = self._mock_children.get(name, _missing)
if obj is _deleted:
raise AttributeError(name)
并将其与 3.7.3+ 代码进行比较:
def __delattr__(self, name):
# ...
obj = self._mock_children.get(name, _missing)
if name in self.__dict__:
_safe_super(NonCallableMock, self).__delattr__(name)
elif obj is _deleted:
raise AttributeError(name)
在重复删除的情况下,3.6 代码会触发 AttributeError
(由于第一个删除设置 _deleted
)——但修复了它以避免这种情况(通过elif
)
我正在使用 unittest.mock
来测试我的代码。如果对象不存在,我创建的函数会使用 hasattr
.
根据 Python 3.6 and newer versions 中的文档,我可以使用 del
删除 mock 的属性,但它在 Python 3.6 上失败了。在 Python 3.7 及更高版本上,它有效。
这是一个简短的测试,展示了我要测试的内容:
from unittest.mock import Mock
def add_attribute(info):
if not hasattr(info, 'attribute'):
info.attribute = 'Attribute Added'
def test_mock():
info = Mock()
assert hasattr(info, 'attribute')
del info.attribute
assert not hasattr(info, 'attribute')
add_attribute(info)
assert info.attribute == 'Attribute Added'
del info.attribute
assert not hasattr(info, 'attribute')
在 python 3.8 上有效:
======= test session starts ========
platform linux -- Python 3.8.5, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /code
plugins: cov-2.12.1
collected 1 item
test.py .
在 python 3.6 上,它失败了:
============================= test session starts ==============================
platform linux -- Python 3.6.14, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /code
plugins: cov-2.12.1
collected 1 item
test.py F [100%]
=================================== FAILURES ===================================
__________________________________ test_mock ___________________________________
def test_mock():
info = Mock()
assert hasattr(info, 'attribute')
del info.attribute
assert not hasattr(info, 'attribute')
add_attribute(info)
assert info.attribute == 'Attribute Added'
> del info.attribute
test.py:14:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <Mock id='140171347130128'>, name = 'attribute'
def __delattr__(self, name):
if name in _all_magics and name in type(self).__dict__:
delattr(type(self), name)
if name not in self.__dict__:
# for magic methods that are still MagicProxy objects and
# not set on the instance itself
return
if name in self.__dict__:
object.__delattr__(self, name)
obj = self._mock_children.get(name, _missing)
if obj is _deleted:
> raise AttributeError(name)
E AttributeError: attribute
/usr/local/lib/python3.6/unittest/mock.py:728: AttributeError
=========================== short test summary info ============================
FAILED test.py::test_mock - AttributeError: attribute
============================== 1 failed in 0.11s ===============================
test_python exited with code 1
我不确定 unittest 库是否已更新,或者它可能是关于如何在 Python 版本之间传递函数的(非)预期行为。
如有任何见解,我们将不胜感激。
这已在 222d303 for issue bpo-20239
中修复这已在 d358a8c which first appeared in python 3.7.3rc1
中移植到 3.7此补丁对 mocks
上的__delattr__
方法进行了重大更改
~<3.7.3代码摘录:
def __delattr__(self, name):
# ...
if name in self.__dict__:
object.__delattr__(self, name)
obj = self._mock_children.get(name, _missing)
if obj is _deleted:
raise AttributeError(name)
并将其与 3.7.3+ 代码进行比较:
def __delattr__(self, name):
# ...
obj = self._mock_children.get(name, _missing)
if name in self.__dict__:
_safe_super(NonCallableMock, self).__delattr__(name)
elif obj is _deleted:
raise AttributeError(name)
在重复删除的情况下,3.6 代码会触发 AttributeError
(由于第一个删除设置 _deleted
)——但修复了它以避免这种情况(通过elif
)