Adding enthought.traits.api.HasTraits as an ancestor using Python metaclasses
Today, while banging my head against a wall trying to get a SWIG-generated Python module to play nice with Python Traits, I cooked a way to use Python metaclasses to modify a class to add enthought.traits.has_traits.HasTraits as a base class:
from enthought.traits.api import HasTraits from enthought.traits.has_traits import MetaHasTraits # See http://onlamp.com/lpt/a/3388 for an intro to Python metaclasses and # http://code.activestate.com/recipes/204197-solving-the-metaclass-conflict/ # for what can go wrong here class AddHasTraitsAncestor(MetaHasTraits): def __new__(cls, name, bases, dct): return MetaHasTraits.__new__(cls, name, cls.modify(bases), dct) def __init__(cls, name, bases, dct): super(AddHasTraitsAncestor, cls).__init__(name, cls.modify(bases), dct) @staticmethod def modify(bases): if (bases == (object,)): return (HasTraits,) else: new_bases = list(bases) new_bases.insert(0, HasTraits) return tuple(new_bases)
Adding the line __metaclass__ = AddHasTraitsAncestor
to a class definition is enough, in some cases, for the class to now have HasTraits as a base class. Most of the frustrating bits were before I realized HasTraits
has its own metaclass MetaHasTraits
which AddHasTraitsAncestor
had to subclass before the approach worked.
"Uhhh...?" you say. That puzzled look asking "Why didn't you just add the base class yourself?". I used this approach because SWIG was generating the Python class definitions and I was abusing SWIG's pythoncode
feature to tweak the SWIG wrappers to be Traits-ready a la
%extend someCPlusPlusClass { %pythoncode %{ __metaclass__ = AddHasTraitsAncestor %} }
Turns out it works and my SWIG wrappers are now Traits-friendly. I'm still working on getting Traits to see the SWIG-based members, however. Suggestions much appreciated.