Larry Hastings wrote:
On Wed, Aug 11, 2021 at 10:32 AM Thomas Grainger <tagrain@gmail.com mailto:tagrain@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,
On 8/11/21 2:48 AM, Jukka Lehtosalo wrote: 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/
You mention that you wanted this to work also for non-type hint usage of annotations, and so a ForwardRef won't work. As such, would you also change this for function decorators so you can do this? ``` @decorator @typing.no_type_check def ham(spam: ham): ... So it means: ``` def ham(spam: ham): ... ham = typing.no_type_check(ham) ham = decorator(ham) ``` Obviously it's meaningless as a type hint, hence the no_type_check opt out