On Sat, Apr 21, 2012 at 11:30 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Sun, Apr 22, 2012 at 12:55 AM, PJ Eby <pje@telecommunity.com> wrote:
> Personally, I think __build_class__ should be explicitly exposed and
> supported, if for no other reason than that it allows one to re-implement
> old-style __metaclass__ support in 2.x modules that rely on it...  and I
> have a lot of those to port.  (Which is why I also think the convenience API
> for PEP 3115-compatible class creation should actually call __build_class__
> itself.  That way, if it's been replaced, then the replaced semantics would
> *also* apply to dynamically-created classes.)

No, we already have one replaceable-per-module PITA like that (i.e.
__import__). I don't want to see us add another one.

Well, it's more like replacing than adding; __metaclass__ has this job in 2.x.  PEP 3115 removed what is (IMO) an important feature: the ability for method-level decorators to affect the class, without needing user-specified metaclasses or class decorators.

This is important for e.g. registering methods that are generic functions, without requiring the addition of redundant metaclass or class-decorator statements, and it's something that's possible in 2.x using __metaclass__, but *not* possible under PEP 3115 without hooking __build_class__.  Replacing builtins.__build_class__ allows the restoration of __metaclass__ support at the class level, which in turn allows porting 2.x code that uses this facility.

To try to be more concrete, here's an example of sorts:

class Foo:
    @decorate(blah, fah)
    def widget(self, spam):

If @decorate needs access to the 'Foo' class object, this is not possible under PEP 3115 without adding an explicit metaclass or class decorator to support it.  And if you are using such method-level decorators from more than one source, you will have to combine their class decorators or metaclasses in some way to get this to work.  Further, if somebody forgets to add the extra metaclass(es) and/or class decorator(s), things will quietly break.

However, under 2.x, a straightforward solution is possible (well, to me it's straightforward) : method decorators can replace the class' __metaclass__ and chain to the previous one, if it existed.  It's like giving method decorators a chance to *also* be a class decorator.

Without some *other* way to do this in 3.x, I don't have much of a choice besides replacing __build_class__ to accomplish this use case.