[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