[Python-Dev] Possible rough edges in Python 3 metaclasses (was Re: Language reference updated for metaclasses)

Nick Coghlan ncoghlan at gmail.com
Tue Jun 5 09:53:05 CEST 2012


On Tue, Jun 5, 2012 at 2:03 PM, PJ Eby <pje at telecommunity.com> wrote:
> On Mon, Jun 4, 2012 at 10:43 PM, Eric Snow <ericsnowcurrently at gmail.com>
> wrote:
>>
>> On Mon, Jun 4, 2012 at 6:10 PM, PJ Eby <pje at telecommunity.com> wrote:
>> > I mean that class-level __metaclass__ is no longer supported as of PEP
>> > 3115,
>> > so I can't use that as a way to non-invasively obtain the enclosing
>> > class at
>> > class creation time.
>>
>> Depends on what you mean by non-invasive:
>
>
> Non-invasive meaning, "not requiring the user of the descriptor or decorator
> to make extra declarations-at-a-distance, especially ones that increase the
> likelihood of clashing machinery if multiple frameworks require the same
> functionality."  ;-)
>
> That means class decorators, mixins, and explicit metaclasses don't work.
> The class decorator adds yak shaving, and the others lead to functional
> clashing.
>
> Currently, my choices for porting these libraries (and their dependents) to
> Python 3 are (in roughly descending order of preference):
>
> 1. Replace __builtins__.__build_class__ and hope PyPy et al follow CPython's
> lead,

Please don't try to coerce everyone else into supporting such an ugly
hack by abusing an implementation detail. There's a reason
types.new_class() uses a different signature. Deliberately attempting
to present python-dev with a fait accompli instead of building
consensus for officially adding a feature to the language is *not
cool*.

> I would prefer to have an option 4 or 5 (where there's a standard Python way
> to get a class creation callback from a class body), but #1 is honestly the
> most attractive at the moment, as I might only need to implement it in *one*
> library, with no changes to clients, including people who've built stuff
> based on that library or any of its clients, recursively.  (#2 would also do
> it, but I was *really* hoping to get rid of that hack in Python 3.)

Please be patient and let us work out a solution that has at least
some level of consensus associated with it, rather than running off
and doing your own thing.

As I understand it, what you currently do is, from a running
decorator, walk the stack with sys._getframes() and insert a
"__metaclass__" value into the class namespace.

Now, one minor annoyance with current class decorators is that they're
*not* inherited. This is sometimes what you want, but sometimes you
would prefer to automatically decorate all subclasses as well.
Currently, that means writing a custom metaclass to automatically
apply the decorators. This has all the problems you have noted with
composability.

It seems then, that a potentially clean solution may be found by
adding a *dynamic* class decoration hook. As a quick sketch of such a
scheme, add the following step to the class creation process (between
the current class creation process, but before the execution of
lexical decorators):

    for mro_cls in cls.mro():
        decorators = mro_cls.__dict__.get("__decorators__", ())
        for deco in reversed(decorators):
            cls = deco(cls)

Would such a dynamic class decoration hook meet your use case? Such a
hook has use cases (specifically involving decorator inheritance) that
*don't* require the use of sys._getframes(), so is far more likely to
achieve the necessary level of consensus.

As a specific example, the unittest module could use it to provide
test parameterisation without needing a custom metaclass.


> Given these choices, I hope it's more understandable why I'd want to lobby
> for at least documenting __build_class__ and its replaceability, and perhaps
> encouraging other Python 3 implementations to offer the same feature.

You've jumped to a hackish solution instead of taking a step back and
trying to think of a potentially elegant addition to the language that
is more composable and doesn't require modification of process global
state. You complain that metaclasses are hard to compose, and your
"solution" is to monkeypatch a deliberately undocumented builtin?

Regards,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-Dev mailing list