我正在为我的python库做一些测试,它应该适用于所有python版本--27,33,34,35,36和 pypy 。在我的测试中,我嘲笑datetime
库并且它们都有效,除非我在pypy
中运行它们(看看为什么我之前加粗了?这样的情节扭曲!)。
这是我遇到麻烦的MCVE:
import mock
import datetime as dtl
mk = mock.Mock(wraps=dtl.datetime)
p = mock.patch('datetime.datetime', mk)
p.start()
from datetime import datetime
d1 = datetime.now()
d2 = datetime.now()
print d1 == d2
在所有python版本中,最后一行返回False
。在pypy
最后一行抛出:
>>>> d1 == d2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/pypy/lib_pypy/datetime.py", line 1764, in __eq__
if isinstance(other, datetime):
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
我挖掘了pypy
的源代码,试图理解这个问题,为什么它和其他python版本之间存在差异,我发现了以下内容:
# Comparisons of datetime objects with other.
def __eq__(self, other):
if isinstance(other, datetime):
return self._cmp(other) == 0
elif hasattr(other, "timetuple") and not isinstance(other, date):
return NotImplemented
else:
return False
这种有道理。 datetime
类现在是Mock
对象 - 而python坚持认为isinstance
的第二个参数是一个类型。 NEATO。
所以我决定查看cpython对datetime
的实现,并惊喜:
# Comparisons of datetime objects with other.
def __eq__(self, other):
if isinstance(other, datetime):
return self._cmp(other, allow_mixed=True) == 0
elif not isinstance(other, date):
return NotImplemented
else:
return False
这里完成了相同的确切验证,但它没有提出任何内容¯\_(ツ)_/¯
。
我将在python 2.7中添加:
>>> d = datetime.now()
>>> d
datetime.datetime(2017, 9, 7, 9, 31, 50, 838155)
>>> d == d
True
>>> datetime
<Mock id='139788521555024'>
>>> isinstance(d, datetime)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
我有两个问题:
__eq__
或任何其他魔术方法的情况下模拟日期时间对象:)非常有责任!
答案 0 :(得分:1)
这是python中的一个真正的问题,为什么使用isinstanceof
这是一个不好的做法。
解决方案是不使用mock进行修补,而是使用继承自datetime.datetime
的类进行修补。但是,因为你将从datetime继承而仍想根据真正的datetime
类检查类型,我们需要覆盖真实类的isinstance
检查。
import datetime as dtl
import mock
real_datetime_class = dtl.datetime
class DatetimeSubclassMeta(type):
"""Datetime mock metaclass to check instancechek to the real class."""
@classmethod
def __instancecheck__(mcs, obj):
return isinstance(obj, real_datetime_class)
class BaseMockedDatetime(real_datetime_class):
"""Mock class to cover datetime class."""
MockedDatetime = DatetimeSubclassMeta('datetime',
(BaseMockedDatetime,),
{})
p = mock.patch('datetime.datetime', MockedDatetime)
p.start()
from datetime import datetime
d = datetime.now()
isinstance(d, datetime)
答案 1 :(得分:0)
至于“为什么”,它可能与CPython中的C实现datetime
,PyPy中的纯python以及Python类对象和内置C语言之间的一些细微差别有关。