On Sat, Apr 23, 2022 at 7:10 PM Paul Moore <p.f.moore@gmail.com> wrote:
On Sat, 23 Apr 2022 at 22:42, Rob Cliffe via Python-Dev
<python-dev@python.org> wrote:
>
> UGH!
>
> I thought there was a general understanding that when typing was added
> to Python, there would be no impact, or at least minimal impact, on
> people who didn't use it.  (Raises hand.)
> Now we see an(other) instance of intention creep.

To be fair, none of this is needed unless you want to add type
annotations to your code. So it's still perfectly possible to ignore
all of this (which is what I am currently doing).

What I am concerned about is when users of libraries I write start to
claim that I "need" to add this sort of stuff to my code, so that they
can type check their code that uses mine, and/or they can get tooltips
for my APIs in their IDEs. That's where I think the biggest issue with
a proposal like this arises - the *social* pressure on people to adopt
typing, and all the complexities it adds. But again, that's not
something that's specific to this proposal, it's inherent in the whole
question of whether people add type annotations at all.

So I'm -1 on this proposal, but just because I fear I may be forced to
use it when I don't need to, rather than because I think it's a bad
idea per se.

Paul



I stand with Paul here -
But as a "heavy  user" of metaclasses, as far as that is possible.
(less in production code or "non toy projects", but a lot in
understanding what goes, answering questions and teaching people
on how they should best use (or skip using) a metaclass.
I'd say this breaks too much of the current working of class creation for, 
possibly, too little.

And - on a second thought: Was not this the kind of thing that PEP 563 was
supposed to fix? Pep 563 is accepted now, I think one may just use a forward
declaration in any annotation context: it will be lazily evaluated and the target
class will have been created. 

So, while I understand the problem with forward references, which have
been historically workaround with the usage of strings (even long before
even Python 3.0, in Django's Models) I believe this is not a good approach


So first part:
==================
I'd be comfortable with the statements and blocks as they are if the 
"forward class" statement would not mess with the metaclass and
class creation at all: just create a place-holder the type checkers could then
use to find the class later on the file, or later on the code, and then use that class.

Has this been thought of? Is there any reason why that would not work, better than
it will put some burden on static-type checkers, as opposed to fundamentally modify the
way classes are built so that any library featuring any metaclass will have
to be divided in incompatible releases "before this pep" and "after this pep"? 
(and with the harsh inconvenience of needing to have one a part of the
code which is often the most complicated 100% rewritten)


Second part:
=============

The proposal as it is does not preserve everything that is possible with 
the current metaclass "__new__", besides complicating things.
Moreover, there _are_ similar steps on "breaking metaclass.`__new__` " that 
would actually be useful in customizing the way classes are created, 
without sacrificing compatibility (in a sense the custom "metaclass.__new__" 
would still be called exactly as it is today).

If there is any chance this thing will move on, I'd like to detail these ideas 
(it would be longish, similar in size with the proto-pep text) - and maybe there is a 
way to reconcile compatibility, without the  proposal of splitting "__new__" 
in two 100% incompatible methods. 

I will leave the actual amendments to this part of the proposal 
(which would affect all the inner workings of the metaclasses as exposed) 
to another message. But I will leave one first, huge,  problem here that:


```Python
     def __new_forward__(metaclass, name, bases, namespace, **kwargs):

     def __new_continue__(metaclass, cls, **kwargs):
```

These two, as they are, do not allow for some of the things that are possible today: 
let's supose I want, in a custom metaclass `__new__` method inspect the 
namespace and creates a "__slots__" declaration based on what already _is_
 on the namespace: in "__new_forward__" the namespace is filled in a non deterministic way and should
 not be inspected , and in "__new_continue__" it had already been 
processed by (what currently is) "type.__new__" and baked into the proxy-map 
inside the "cls". I the specific case of creating "__slots__" in a similar way "@dataclass" 
does, the oportunity is gone.

I won't mention the suggestion in the text that "class transformers that would 
create slots are free to re-create the class object upon its first instantiation" 
is _rather_  bogus. I can imagine a metaclass "__call__" method that could do 
that, but then, the classes the instances would be actually using  would rather 
become either have to be kept as an attribute in the class as declared in the 
code, or kept in a registry in the metaclass: in both cases, calls to "isinstance" 
and "issubclass" involving this class would have to be customized. There are 
hooks for that, so it _would_ be possible - but we are talking of 3 lines inside 
an overriden "__new__" method in a custom metaclass that would necessarily 
require the creation of a "proxy real class in use managing system", involving 
the customization of the metaclass "__call__", "__instancecheck__" and "__subclasscheck__".
and the creation of a shadow-class for each class.

 Also note that unless customized for that, no static type-checker would 
ever know of this shadow class - and it is actually different from the declared 
class in the code (for example, it has "__slots__" active)

So, sorry if the two paragraphs above look like "an extreme inprobable work around",
but it is what is implicit, even somewhat explicit, in the PEP with the suggestion
of how "@dataclass" should be made to work. 

Actually, modifying the namespace before having it "baked" into
the cls "real namespace" through "super().__new__" call is one
of the few things metaclasses customization have any use for nowadays
(past the __init_subclass__ creation) 


The idea of creating a proxy object in the "forward declaration" possibly 
calling "__prepare__"  and keep metaclasses as they are, with the "__new__" 
method to be called just after the "continue class" block is executed,  are _much_ more attractive. 

(again, If, and only if this is gonna be pursued in anyway -
 I may be wrong, but I think that PEP 563 makes this a non issue at all)

 

   js
 -><-
 

PS To be open here, I do actually like type annotations in
straightforward situations - they have located some bugs in my code
for me, and tooltips in VS code *are* nice. What I don't like is not
being able to stick to simple stuff and not bother annotating the
complicated bits, or being pushed into over-strict annotations because
it's too hard (or verbose) to express dynamic, duck-typed constraints.


also, yes!