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.
_______________________________________________
pypy-issue mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-issue