Python3 test cases (doctests) are failing with my sample code. But the same is working fine in Python2.
test.py
class Test(object):
def __init__(self, a=0):
self.a = a
def __getattr__(self, attr):
return Test(a=str(self.a) + attr)
tst.py
from test import Test
t = Test()
Run test cases: python3 -m doctest -v tst.py
Error:
Traceback (most recent call last):
File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/usr/lib/python3.6/doctest.py", line 2787, in <module>
sys.exit(_test())
File "/usr/lib/python3.6/doctest.py", line 2777, in _test
failures, _ = testmod(m, verbose=verbose, optionflags=options)
File "/usr/lib/python3.6/doctest.py", line 1950, in testmod
for test in finder.find(m, name, globs=globs, extraglobs=extraglobs):
File "/usr/lib/python3.6/doctest.py", line 933, in find
self._find(tests, obj, name, module, source_lines, globs, {})
File "/usr/lib/python3.6/doctest.py", line 992, in _find
if ((inspect.isroutine(inspect.unwrap(val))
File "/usr/lib/python3.6/inspect.py", line 513, in unwrap
raise ValueError('wrapper loop when unwrapping {!r}'.format(f))
ValueError: wrapper loop when unwrapping <test.Test object at 0x7f6e80028550>
Can anyone please help in how to overcome this error.
Thanks.
For unittest.mock
try importing items as
from unittest import mock
instead of
from unittest.mock import patch
This worked around the bug for me.
This is arguably a bug in doctest. What's happening is that doctest is searching for functions/methods/callables with a docstring, and while doing so it's unwrapping any decorators it finds. Why it does this, I have no idea. But anyway, doctest ends up calling inspect.unwrap(t)
(where t
is a Test
instance), which is essentially equivalent to doing this:
while True:
try:
t = t.__wrapped__
except AttributeError:
break
Because t
is a Test
instance, accessing t.__wrapped__
calls __getattr__
and returns a new Test
instance. This would go on forever, but inspect.unwrap
is smart enough to notice that it's not getting anywhere, and throws an exception instead of entering an infinite loop.
As a workaround, you can rewrite your __getattr__
method to throw an AttributeError
when __wrapped__
is accessed. Even better, throw an AttributeError when any dunder-attribute is accessed:
def __getattr__(self, attr):
if attr.startswith('__') and attr.endswith('__'):
raise AttributeError
return Test(a=str(self.a) + attr)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With