Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #77653 > unrolled thread

__qualname__ in python 3.3

Started byISE Development <isenntp@gmail.com>
First post2014-09-06 18:13 +0200
Last post2014-09-08 00:25 +0200
Articles 6 — 3 participants

Back to article view | Back to comp.lang.python


Contents

  __qualname__ in python 3.3 ISE Development <isenntp@gmail.com> - 2014-09-06 18:13 +0200
    Re: __qualname__ in python 3.3 Peter Otten <__peter__@web.de> - 2014-09-06 18:47 +0200
      Re: __qualname__ in python 3.3 ISE Development <isenntp@gmail.com> - 2014-09-06 19:09 +0200
        Re: __qualname__ in python 3.3 Peter Otten <__peter__@web.de> - 2014-09-06 20:11 +0200
    Re: __qualname__ in python 3.3 Antoine Pitrou <antoine@python.org> - 2014-09-07 13:03 +0000
      Re: __qualname__ in python 3.3 ISE Development <isenntp@gmail.com> - 2014-09-08 00:25 +0200

#77653 — __qualname__ in python 3.3

FromISE Development <isenntp@gmail.com>
Date2014-09-06 18:13 +0200
Subject__qualname__ in python 3.3
Message-ID<540b3293$0$2056$426a74cc@news.free.fr>
Hi,

When a class is defined within a function, the class generation function's 
'__qualname__' attrbute is not qualified a name.

For instance:

    def test():
        class T:
            def method(self):
                pass
        t = T()
        t.method()

When tracing a call to 'test()' using 'sys.settrace()', extracting the 
'code' object from the frames of 'call' events and matching it to a 
'function' object (using 'gc.get_referrers()') results in the following:

'code' object     'function' object
----------------  ------------------------------------
co_name: test     __qualname__: test
co_name: T        __qualname__: T
co_name: method   __qualname__: test.<locals>.T.method

The second call corresponds to the class definition and not the call to the 
constructor (which is in fact a call to 'object.__init__', a C function 
hence not traced as a 'call' event - I checked this by disassembling the 
code object).

I would expect the second call's '__qualname__' to be 'test.<locals>.T'. Can 
this be considered a bug? If so, I'll open one.

-- isedev

[toc] | [next] | [standalone]


#77655

FromPeter Otten <__peter__@web.de>
Date2014-09-06 18:47 +0200
Message-ID<mailman.13836.1410022089.18130.python-list@python.org>
In reply to#77653
ISE Development wrote:

> Hi,
> 
> When a class is defined within a function, the class generation function's
> '__qualname__' attrbute is not qualified a name.
> 
> For instance:
> 
>     def test():
>         class T:
>             def method(self):
>                 pass
>         t = T()
>         t.method()
> 
> When tracing a call to 'test()' using 'sys.settrace()', extracting the
> 'code' object from the frames of 'call' events and matching it to a
> 'function' object (using 'gc.get_referrers()') results in the following:
> 
> 'code' object     'function' object
> ----------------  ------------------------------------
> co_name: test     __qualname__: test
> co_name: T        __qualname__: T
> co_name: method   __qualname__: test.<locals>.T.method
> 
> The second call corresponds to the class definition and not the call to
> the constructor (which is in fact a call to 'object.__init__', a C
> function hence not traced as a 'call' event - I checked this by
> disassembling the code object).
> 
> I would expect the second call's '__qualname__' to be 'test.<locals>.T'.
> Can this be considered a bug? If so, I'll open one.

I don't understand what you are doing, so I tried to reproduce the 
unqualified class name in 3.4 with the simpler approach of returning T:

Python 3.4.0 (default, Apr 11 2014, 13:05:11) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def test():
...     class T:
...         def method(self): pass
...     return T
... 
>>> T = test()
>>> T.__qualname__
'test.<locals>.T'
>>> T.method.__qualname__
'test.<locals>.T.method'

If you do it that way with 3.3 (I don't have it handy) do you still see
T instead of test.<locals>.T?

[toc] | [prev] | [next] | [standalone]


#77657

FromISE Development <isenntp@gmail.com>
Date2014-09-06 19:09 +0200
Message-ID<540b3fe4$0$2055$426a74cc@news.free.fr>
In reply to#77655
Peter Otten wrote:

> ISE Development wrote:
> 
>> Hi,
>> 
>> When a class is defined within a function, the class generation
>> function's '__qualname__' attrbute is not qualified a name.
>> 
>> For instance:
>> 
>>     def test():
>>         class T:
>>             def method(self):
>>                 pass
>>         t = T()
>>         t.method()
>> 
>> When tracing a call to 'test()' using 'sys.settrace()', extracting the
>> 'code' object from the frames of 'call' events and matching it to a
>> 'function' object (using 'gc.get_referrers()') results in the following:
>> 
>> 'code' object     'function' object
>> ----------------  ------------------------------------
>> co_name: test     __qualname__: test
>> co_name: T        __qualname__: T
>> co_name: method   __qualname__: test.<locals>.T.method
>> 
>> The second call corresponds to the class definition and not the call to
>> the constructor (which is in fact a call to 'object.__init__', a C
>> function hence not traced as a 'call' event - I checked this by
>> disassembling the code object).
>> 
>> I would expect the second call's '__qualname__' to be 'test.<locals>.T'.
>> Can this be considered a bug? If so, I'll open one.
> 
> I don't understand what you are doing, so I tried to reproduce the
> unqualified class name in 3.4 with the simpler approach of returning T:
> 
> Python 3.4.0 (default, Apr 11 2014, 13:05:11)
> [GCC 4.8.2] on linux
> Type "help", "copyright", "credits" or "license" for more information.
>>>> def test():
> ...     class T:
> ...         def method(self): pass
> ...     return T
> ...
>>>> T = test()
>>>> T.__qualname__
> 'test.<locals>.T'
>>>> T.method.__qualname__
> 'test.<locals>.T.method'
> 
> If you do it that way with 3.3 (I don't have it handy) do you still see
> T instead of test.<locals>.T?

Python 3.3 behaves in the same way in that case.

This following shows the behaviour I am referring to:

    import gc
    import sys
    import inspect

    def global_trace(frame,event,arg):
        if event == 'call':
            code = frame.f_code
            funcs = [obj for obj in gc.get_referrers(code)
                     if inspect.isfunction(obj)]
            if len(funcs) == 1:
                f = funcs[0]
                print(f.__qualname__)

    def test():
        class C:
            def method(self):
                self
        c = C()
        c.method()

    sys.settrace(global_trace)
    try:
        test()
    finally:
        sys.settrace(None)

which produces:

    test
    C
    test.<locals>.C.method

[toc] | [prev] | [next] | [standalone]


#77660

FromPeter Otten <__peter__@web.de>
Date2014-09-06 20:11 +0200
Message-ID<mailman.13840.1410027079.18130.python-list@python.org>
In reply to#77657
ISE Development wrote:

> Peter Otten wrote:
> 
>> ISE Development wrote:
>> 
>>> Hi,
>>> 
>>> When a class is defined within a function, the class generation
>>> function's '__qualname__' attrbute is not qualified a name.
>>> 
>>> For instance:
>>> 
>>>     def test():
>>>         class T:
>>>             def method(self):
>>>                 pass
>>>         t = T()
>>>         t.method()
>>> 
>>> When tracing a call to 'test()' using 'sys.settrace()', extracting the
>>> 'code' object from the frames of 'call' events and matching it to a
>>> 'function' object (using 'gc.get_referrers()') results in the following:
>>> 
>>> 'code' object     'function' object
>>> ----------------  ------------------------------------
>>> co_name: test     __qualname__: test
>>> co_name: T        __qualname__: T
>>> co_name: method   __qualname__: test.<locals>.T.method
>>> 
>>> The second call corresponds to the class definition and not the call to
>>> the constructor (which is in fact a call to 'object.__init__', a C
>>> function hence not traced as a 'call' event - I checked this by
>>> disassembling the code object).
>>> 
>>> I would expect the second call's '__qualname__' to be 'test.<locals>.T'.
>>> Can this be considered a bug? If so, I'll open one.
>> 
>> I don't understand what you are doing, so I tried to reproduce the
>> unqualified class name in 3.4 with the simpler approach of returning T:
>> 
>> Python 3.4.0 (default, Apr 11 2014, 13:05:11)
>> [GCC 4.8.2] on linux
>> Type "help", "copyright", "credits" or "license" for more information.
>>>>> def test():
>> ...     class T:
>> ...         def method(self): pass
>> ...     return T
>> ...
>>>>> T = test()
>>>>> T.__qualname__
>> 'test.<locals>.T'
>>>>> T.method.__qualname__
>> 'test.<locals>.T.method'
>> 
>> If you do it that way with 3.3 (I don't have it handy) do you still see
>> T instead of test.<locals>.T?
> 
> Python 3.3 behaves in the same way in that case.
> 
> This following shows the behaviour I am referring to:
> 
>     import gc
>     import sys
>     import inspect
> 
>     def global_trace(frame,event,arg):
>         if event == 'call':
>             code = frame.f_code
>             funcs = [obj for obj in gc.get_referrers(code)
>                      if inspect.isfunction(obj)]
>             if len(funcs) == 1:
>                 f = funcs[0]
>                 print(f.__qualname__)
> 
>     def test():
>         class C:
>             def method(self):
>                 self
>         c = C()
>         c.method()
> 
>     sys.settrace(global_trace)
>     try:
>         test()
>     finally:
>         sys.settrace(None)
> 
> which produces:
> 
>     test
>     C
>     test.<locals>.C.method

OK, I get the same output in Python 3.4. That C seems to be an internal 
function that helps build the class test.<locals>.C, and I have no 
expectations as to its name. If anything I'd use something completely 
different, _init_namespace_C, say.

I'm sorry I am not familiar enough with Python's internals to answer your 
question -- but if in doubt, file a report.

[toc] | [prev] | [next] | [standalone]


#77670

FromAntoine Pitrou <antoine@python.org>
Date2014-09-07 13:03 +0000
Message-ID<mailman.13847.1410094999.18130.python-list@python.org>
In reply to#77653
Hi,

ISE Development <isenntp <at> gmail.com> writes:
> 'code' object     'function' object
> ----------------  ------------------------------------
> co_name: test     __qualname__: test
> co_name: T        __qualname__: T
> co_name: method   __qualname__: test.<locals>.T.method
> 
> The second call corresponds to the class definition and not the call to the 
> constructor (which is in fact a call to 'object.__init__', a C function 
> hence not traced as a 'call' event - I checked this by disassembling the 
> code object).

There's nothing wrong here. That's just the way things are implemented
internally. This may change in the future without prior notice, so
you shouldn't rely on it.

If you want to dig more, you have to look at how the hidden function ("T")
works:

>>> def f():
...   class T: pass
... 
>>> f.__code__.co_consts
(None, <code object T at 0x7f4d9d0f4a00, file "<stdin>", line 2>, 'T')
>>> dis.dis(f.__code__.co_consts[1])
  2           0 LOAD_NAME                0 (__name__)
              3 STORE_NAME               1 (__module__)
              6 LOAD_CONST               0 ('f.<locals>.T')
              9 STORE_NAME               2 (__qualname__)
             12 LOAD_CONST               1 (None)
             15 RETURN_VALUE

Regards

Antoine.

[toc] | [prev] | [next] | [standalone]


#77688

FromISE Development <isenntp@gmail.com>
Date2014-09-08 00:25 +0200
Message-ID<540cdb4f$0$2926$426a74cc@news.free.fr>
In reply to#77670
Antoine Pitrou wrote:
> Hi,
> 
> ISE Development <isenntp <at> gmail.com> writes:
>> 'code' object     'function' object
>> ----------------  ------------------------------------
>> co_name: test     __qualname__: test
>> co_name: T        __qualname__: T
>> co_name: method   __qualname__: test.<locals>.T.method
>> 
>> The second call corresponds to the class definition and not the call to
>> the constructor (which is in fact a call to 'object.__init__', a C
>> function hence not traced as a 'call' event - I checked this by
>> disassembling the code object).
> 
> There's nothing wrong here. That's just the way things are implemented
> internally. This may change in the future without prior notice, so
> you shouldn't rely on it.
> 
> If you want to dig more, you have to look at how the hidden function ("T")
> works:
> 
>>>> def f():
> ...   class T: pass
> ...
>>>> f.__code__.co_consts
> (None, <code object T at 0x7f4d9d0f4a00, file "<stdin>", line 2>, 'T')
>>>> dis.dis(f.__code__.co_consts[1])
>   2           0 LOAD_NAME                0 (__name__)
>               3 STORE_NAME               1 (__module__)
>               6 LOAD_CONST               0 ('f.<locals>.T')
>               9 STORE_NAME               2 (__qualname__)
>              12 LOAD_CONST               1 (None)
>              15 RETURN_VALUE
> 
> Regards
> 
> Antoine.

Ok, I accept it's implementation specific. That's fair enough.

Yet wouldn't it make more sense to have the 'T' function '__qualname__' 
attribute refer to the defining context, i.e. in the present case, 
'test.<locals>.T' (much along the lines of the actual method qualified 
names, e.g. 'test.<locals>.T.__init__', and identical to the qualified name 
of the actual object if returned by the defining function - see Peter Otten 
reply)?

The rationale is that a properly qualified name is easier to interpret than 
the current unqualified one. To properly identify the 'T' function if a 
'class T' is defined in more than enclosing function requires some 
additional complex (and hence error prone) logic as things stand: one has to 
examine the previous stack frame.

Effectively, I am not saying the current behaviour is wrong, simply that it 
is inconsistent and could be improved. In that context, is it worth an 
enhancement request?

-- isedev

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web