<div dir="ltr">Hi all,<br><div><div><br class="inbox-inbox-Apple-interchange-newline">tl;dr: I propose adding a `register()` decorator, to be used like this:<br><br></div><div>    @abc.register(Abc1, Abc2)<br>    class D:</div></div><div>        ...</div><div><div><div><br class="inbox-inbox-Apple-interchange-newline">For preexisting classes I propose adding a magic static variable `__registered__`, to be handled by ABCMeta:</div><div><br></div>    class Abc1(metaclass=ABCMeta):</div><div>        __registered__ = [D]<br><br class="inbox-inbox-Apple-interchange-newline"></div></div><div>Both forms has the effects of calling ABCMeta.register with the corresponding parameters.</div><div><br></div><div>Explanation:</div><div><br></div>It is possible to register an abstract base class B for preexisting classes A using B.register(A). This form is very flexible - probably too flexible, since the operation changes the type of A, and doing it dynamically makes it difficult for type checker (and human beings) to analyze.<div><br></div><div>But what about new classes? Registering a new class is possible using B.register as a decorator, since it already its argument:</div><div><div><br></div><div>    @Iterable.register</div><div>    @Sequence.register<br>    class A: pass</div><div><br>However, due to restrictions on the expressions allowed inside a decorator, this would not work with generic ABCs, which are common and heavily used in type-checked projects:</div><div><br></div><div><div>    >>> @Iterable[str].register</div><div>      File "<stdin>", line 1</div><div>        @Iterable[str].register</div><div>                        ^</div><div>    SyntaxError: invalid syntax</div></div><div><br></div><div>While it might be possible to infer the argument, it is not always the case, and adds additional unnecessary burden to type-checker and to the reader; discouraging its use. This kind of restrictions may also prevent some other conceivable forms, such as `@type(X).register`.</div><div><br></div><div>Additionally, `abc.register()` is also easier to analyze as a "syntactic construct" from the point of view the type checker, similar to the way checkers handle some other constructs such as `enum()`, `cast()` and others.</div><div><br></div><div>Finally, there's uninformative repetition of `register` for multiple ABCs, and waste of vertical screen space.</div></div><br class="inbox-inbox-Apple-interchange-newline"><div>Q: Why not subclass B?</div><div>A: Since it forces the metaclass of A to be (a subclass of) ABCMeta, which can cause inconsistency in metaclass structure for derived classes. This issue can surface when using Enum (whose metaclass is EnumMeta):<br></div><div><div><br><div>    >>> class A(Iterable, Enum): pass</div><div>    ...</div><div>    Traceback (most recent call last):</div><div>      File "<stdin>", line 1, in <module></div><div>    TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases</div><div><br>This causes problems in typeshed, since it uses inheritance to register `str` as a `Sequence[str]`, for example. As a result, it affects users of mypy trying to define `class A(str, Enum): ...`</div><div><br></div></div><div>Q: Why not add to the typing module?</div><div>A: Because registering an abstract base class has runtime meaning, such as the result of `isinstance` checks.</div><div><br></div><div>Q: Why not add to the standard library? why not some 3rd party module?<br></div><div>A: <br>1. Because dynamic usage should be discouraged, similarly to monkey-patching. Note that, like monkey-patching, this has runtime implications which cause "actions from distance".<br>2. Because it should be used by typeshed and mypy.</div></div><div><div><br></div><div>The implementation of register() is straightforward:<br></div></div><div><br></div><div>    def register(*abcs):</div><div>        def inner(cls):</div><div>            for b in abcs:</div><div>                b.register(cls)</div><div>            return cls<br>        return inner<br><br><br>Thanks,</div><div>Elazar<br><br></div></div>