9

I have an esoteric question involving Python metaclasses. I am creating a Python package for web-server-side code that will make it easy to access arbitrary Python classes via client-side proxies. My proxy-generating code needs a catalog of all of the Python classes that I want to include in my API. To create this catalog, I am using the __metaclass__ special attribute to put a hook into the class-creation process. Specifically, all of the classes in the "published" API will subclass a particular base class, PythonDirectPublic, which itself has a __metaclass__ that has been set up to record information about the class creation.

So far so good. Where it gets complicated is that I want my PythonDirectPublic itself to inherit from a third-party class (enthought.traits.api.HasTraits). This third-party class also uses a __metaclass__.

So what's the right way of managing two metaclasses? Should my metaclass be a subclass of Enthought's metaclass? Or should I just invoke Enthought's metaclass inside my metaclass's __new__ method to get the type object that I will return? Or is there some other mystical incantation to use in this particular circumstance?

Dan Menes
  • 6,667
  • 1
  • 33
  • 35

2 Answers2

5

Should my metaclass be a subclass of Enthought's metaclass?

I believe that is in fact your only choice. If the metaclass of a derived class is not a subclass of the metaclasses of all of its bases then Python will throw a TypeError when you try to create the derived class. So your metaclass for PythonDirectPublic should look something like

class DerivedMetaClass(BaseMetaClass):
    def __new__(cls, name, bases, dct):
        # Do your custom memory allocation here, if any

        # Now let base metaclass do its memory allocation stuff
        return BaseMetaClass.__new__(cls, name, bases, dct)

    def __init__(cls, name, bases, dct):
        # Do your custom initialization here, if any
        # This, I assume, is where your catalog creation stuff takes place

        # Now let base metaclass do its initialization stuff
        super(DerivedMetaClass, cls).__init__(name, bases, dct)

If you don't have access to the definition of the metaclass for your third-party base class, you can replace BaseMetaClass with enthought.traits.api.HasTraits.__metaclass__. It's wordy, but it will work.

Peter Milley
  • 2,768
  • 19
  • 18
1

Specifically, all of the classes in the "published" API will subclass a particular base class, PythonDirectPublic

Rather than adding another metaclass, you could recursively use the result of PythonDirectPublic.subclasses().

Isvara
  • 3,403
  • 1
  • 28
  • 42
  • I'm not finding any documentation on that particular method. Are you suggesting using HasTraits.trait_subclasses()? That could indeed have provided an alternative approach to building the catalog. – Dan Menes Feb 03 '11 at 21:54
  • @DanMenes I believe Dan Ellis actually refers to the `__subclasses__` method, see [this answer](http://stackoverflow.com/a/3862957/396967) for how to use it. – kynan Jun 29 '12 at 10:34
  • @kynan Got it. I think that would work, too. Although its been a couple of years since i looked at this code, so I don't remember all of the wrinkles – Dan Menes Jun 29 '12 at 13:16