On 8/11/21 2:48 AM, Jukka Lehtosalo wrote:
On Wed, Aug 11, 2021 at 10:32 AM Thomas Grainger <tagr...@gmail.com <mailto:tagr...@gmail.com>> wrote:

    Larry Hastings wrote:
    > On 8/11/21 12:02 AM, Thomas Grainger wrote:
    > > I think as long as there's a test case for something like
    > > @dataclass
    > > class Node:
    > >      global_node: ClassVar[Node | None]
    > >      left: InitVar[Node | None]
    > >      right: InitVar[None | None]
    > >
    > > the bug https://bugs.python.org/issue33453
    <https://bugs.python.org/issue33453> and the current
    implementation
    
https://github.com/python/cpython/blob/bfc2d5a5c4550ab3a2fadeb9459b4bd948ff6.
    
<https://github.com/python/cpython/blob/bfc2d5a5c4550ab3a2fadeb9459b4bd948ff6.>..
    shows this is a tricky problem
    > > The most straightforward workaround for this is to skip the
    decorator
    > syntax.  With PEP 649 active, this code should work:
    > class Node:
    >          global_node: ClassVar[Node | None]
    >          left: InitVar[Node | None]
    >          right: InitVar[None | None]
    >     Node = dataclass(Node)
    > //arry/

    the decorator version simply has to work


I also think that it would be unfortunate if the decorator version wouldn't work. This is a pretty basic use case.


So, here's an idea, credit goes to Eric V. Smith.  What if we tweak how decorators work, /juuuust sliiiightly/, so that they work like the workaround code above?

Specifically: currently, decorators are called just after the function or class object is created, before it's bound to a variable.  But we could change it so that we first bind the variable to the initial value, then call the decorator, then rebind.  That is, this code:

   @dekor8
   class C:
        ...

would become equivalent to this code:

   class C:
        ...
   C = dekorate(C)

This seems like it would solve the class self-reference problem--the "Node" example above--when PEP 649 is active.

This approach shouldn't break reasonable existing code.  That said, this change would be observable from Python, and pathological code could notice and break.  For example:

   def ensure_Foo_is_a_class(o):
        assert isinstance(Foo, type)
        return o

   class Foo:
        ...

   @ensure_Foo_is_a_class
   def Foo():
        ...

This terrible code currently would not raise an assertion.  But if we made the proposed change to the implementation of decorators, it would.  I doubt anybody does this sort of nonsense, I just wanted to fully flesh out the topic.


If this approach seems interesting, here's one wrinkle to iron out.  When an object has multiple decorators, would we want to re-bind after each decorator call?  That is, would

   @dekor1
   @dekor2
   @dekor3
   class C:
        ...

turn into approach A:

   class C:
        ...
   C = dekor1(dekor2(dekor3(C)))

or approach B:

   class C:
        ...
   C = dekor3(C)
   C = dekor2(C)
   C = dekor1(C)

I definitely think "approach B" makes more sense.


//arry/

_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/OA4J23TU3XACE5NAUUQNQQ52BXGNHUIS/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to