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
-><-