Opened 5 years ago
Last modified 5 years ago
#30546 closed Bug
SimpleLazyObject not so lazy on Python3 — at Version 1
Reported by: | Sławek Ehlert | Owned by: | nobody |
---|---|---|---|
Component: | Utilities | Version: | dev |
Severity: | Normal | Keywords: | SimpleLazyObject lazy evaluation |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
When running this little script:
from django.utils.functional import * def a_func(): raise Exception('naah') slo = SimpleLazyObject(a_func) assert not isinstance(slo, type)
I'd expect the assertion to pass. It works as I'd expect it to work on Django 1.11 and Python 2.7. However, it does blow up with the naah
exception on both Django 1.11 and 2.2 on Python 3.5 (also on Python 3.6 and 3.7) with the following traceback:
Traceback (most recent call last): File "<input>", line 1, in <module> assert not isinstance(slo, type) File "... /lib/python3.5/site-packages/django/utils/functional.py", line 238, in inner self._setup() File "... /lib/python3.5/site-packages/django/utils/functional.py", line 386, in _setup self._wrapped = self._setupfunc() File "<input>", line 2, in a_func raise Exception('naah') Exception: naah
I'm not sure if this is by design, but the behaviour is certainly inconsistent on Py3 vs. Py2.
I found this problem while trying to run our test suite on Python 3. Unittest framework does an obvious if isinstance(obj, type) and issubclass(obj, case.TestCase):
check when collecting the test suite (see https://github.com/python/cpython/blob/v3.5.7/Lib/unittest/loader.py#L122), so when we have a module-level SimpleLazyObject
(obviously it wraps a different function than the one I've given here, but also a failing one) it gets unexpectedly evaluated and raises an exception, which prevents us from even gathering the tests on Python 3.
I'm not really sure why this is happening. After some brief debugging, it looks like isinstance
function on Python 3 is accessing the __class__
attribute (which is wrapped with the new_method_proxy
function - see https://github.com/django/django/blob/2.2.2/django/utils/functional.py#L348) causing evaluation of the underlying function, whereas on Python 2.7 isinstance
doesn't seem to do that.