Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ValueError: wrapper loop when unwrapping

Tags:

python

doctest

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.

like image 431
Ram Idavalapati Avatar asked Mar 14 '18 07:03

Ram Idavalapati


2 Answers

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.

like image 179
Allison Avatar answered Sep 20 '22 06:09

Allison


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)
like image 20
Aran-Fey Avatar answered Sep 21 '22 06:09

Aran-Fey