[pypy-issue] Issue #1928: Incorrect name lookup occurring in class defined inside of a function. (pypy/pypy)
Graham Dumpleton
issues-reply at bitbucket.org
Thu Nov 20 06:36:51 CET 2014
New issue 1928: Incorrect name lookup occurring in class defined inside of a function.
https://bitbucket.org/pypy/pypy/issue/1928/incorrect-name-lookup-occurring-in-class
Graham Dumpleton:
Sorry, but I can't really supply a direct runnable test case that is going to trigger the bug, as the problem only manifests at some point after the JIT kicks in in some way. As such, it only triggers when run as part of a larger test suite we run under tox. At least that it relates to the JIT kicking in is all I can think of as to why when run in isolation there is no problem.
Anyway, the test program is shown below. I can't pair it back any further as when I start taking more out, even under our larger test suite it starts working okay, so is the minimal I can get such that still creates the problem. It needs pytest and wrapt to be installed.
```
#!python
import pytest
from wrapt import transient_function_wrapper
def override_application_name(name):
class Application(object):
@property
def name(self):
print('locals()', locals())
print('type(name)', type(name))
print('repr(name)', repr(name))
print('type(Application.name)', type(Application.name))
print('repr(Application.name)', repr(Application.name))
return name
@transient_function_wrapper(__name__, 'function_1')
def _override_application_name(wrapped, instance, args, kwargs):
def _bind_params(application, *args, **kwargs):
return application, args, kwargs
application, _args, _kwargs = _bind_params(*args, **kwargs)
application = Application()
return wrapped(application, *_args, **_kwargs)
return _override_application_name
def function_1(obj):
return obj
class Application(object):
name = 'yyy'
@pytest.mark.parametrize(('a',), [(1,)])
def test_bug_many_3(a):
@override_application_name('xxx')
def test_bug():
obj = function_1(Application())
print('type(obj)', type(obj))
print('obj.name', obj.name)
assert isinstance(obj.name, str)
test_bug()
```
So when run standalone using pypy 2.4.0, 2.2.1 and 1.9.0 this test works fine. But when run as part of a larger test suite under tox it fails.
The output we get is:
```
test_pypy_bug.py::test_bug_many_3[1] FAILED
======================================= FAILURES =======================================
__________________________________ test_bug_many_3[1] __________________________________
a = 1
@pytest.mark.parametrize(('a',), [(1,)])
def test_bug_many_3(a):
@override_application_name('xxx')
def test_bug():
obj = function_1(Application())
print('type(obj)', type(obj))
print('obj.name', obj.name)
assert isinstance(obj.name, str)
> test_bug()
test_pypy_bug.py:42:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.tox/pypy-without-extensions/site-packages/wrapt/wrappers.py:517: in __call__
args, kwargs)
.tox/pypy-without-extensions/site-packages/wrapt/wrappers.py:800: in _execute
return wrapped(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
@override_application_name('xxx')
def test_bug():
obj = function_1(Application())
print('type(obj)', type(obj))
print('obj.name', obj.name)
> assert isinstance(obj.name, str)
E assert isinstance(<property object at 0x000000010a232410>, str)
E + where <property object at 0x000000010a232410> = <test_pypy_bug.Application object at 0x000000010a207de0>.name
test_pypy_bug.py:41: AssertionError
--------------------------------- Captured stdout call ---------------------------------
('type(obj)', <class 'test_pypy_bug.Application'>)
('locals()', {'Application': <class 'test_pypy_bug.Application'>, 'self': <test_pypy_bug.Application object at 0x000000010a207de0>, 'name': <property object at 0x000000010a232410>})
('type(name)', <type 'property'>)
('repr(name)', '<property object at 0x000000010a232410>')
('type(Application.name)', <type 'property'>)
('repr(Application.name)', '<property object at 0x000000010a232410>')
('obj.name', <property object at 0x000000010a232410>)
('locals()', {'Application': <class 'test_pypy_bug.Application'>, 'self': <test_pypy_bug.Application object at 0x000000010a207de0>, 'name': <property object at 0x000000010a232410>})
('type(name)', <type 'property'>)
('repr(name)', '<property object at 0x000000010a232410>')
('type(Application.name)', <type 'property'>)
('repr(Application.name)', '<property object at 0x000000010a232410>')
============================== 1 failed in 19.40 seconds ===============================
ERROR: InvocationError: '/Users/graham/Work/python_agent/tests/agent_features/.tox/pypy-without-extensions/bin/py.test -v test_pypy_bug.py'
_______________________________________ summary ________________________________________
ERROR: pypy-without-extensions: commands failed
```
For this code, when looking up the 'name' attribute from inside of the 'name' property method of the Application class which is nested inside of a function, it should be finding the 'name' argument of override_application_name().
As I said, this works fine as standalone test, but in larger test suite, the behaviour changes, and rather than return the 'name' argument of the outer function, it is using the 'name' property method. This is regardless of the fact that 'self' was not used and so it should not have been doing that.
I have added debug in so even if you can't manage to get it to trigger by filling the test file with lots of other code which is run first and so enables the JIT, that you can see that the lookup is wrong and so work from that.
The obvious workaround for this is not to use 'name' as the name of the outer function argument at the same time as calling the property method 'name'. So changing the outer argument to 'app_name' and making the return statement use that as well, then all works fine. Issue is only when the names for argument and property method are the same.
More information about the pypy-issue
mailing list