Imposing metaclass on library
Michele Simionato
mis6 at pitt.edu
Wed Dec 18 14:15:27 EST 2002
Lulu of the Lotus-Eaters <mertz at gnosis.cx> wrote in message news:<mailman.1040183554.30743.python-list at python.org>...
> I am having a problem with metaclass resolution order (I think).
>
> Here is the situation: I have a library of classes, and I do not want
> to modify the library. But I would like to cause all -descendents- of
> library classes to be created with a custom metaclass (within my
> application that uses the library).
>
> The library can look like (trivial case):
>
> % cat library.py
> class Base1(object): pass
> class Base2(object): pass
>
> Here is the application:
>
> % cat application.py
> class Meta(type):
> def __init__(cls, name, bases, dict):
> print "Making", name
> super(Meta, cls).__init__(name, bases, dict)
>
> # Basic tries at using metaclass Meta
> __metaclass__ = Meta
> class App1(object): pass # Doesn't use Meta!
> class App2: pass # Uses Meta
> class App3(object): # Also uses Meta
> __metaclass__ = Meta
>
> # Now I want to create library children with metaclass Meta
> from library import *
> class App4(Base1): pass # No go (even though global __metaclass__)
> class App5(Base1): pass # No go ...
> class App6(Base2): pass # No go ...
>
> # Here's a clumsy way to force it
> class Base1(Base1): __metaclass__ = Meta
> class App7(Base1): pass # Works with redefined Base1
> class App8(Base1): pass # ditto
>
> # Less forced looking attempt that fails.
> Base2.__metaclass__ = Meta
> class App9(Base2): pass # But haven't done anything for Base2
>
> The result is:
>
> % python application.py
> Making App2
> Making App3
> Making Base1
> Making App7
> Making App8
>
> What I really want is some way to force ALL the AppN classes to be
> created using Meta. In my particular application, there will be a large
> number of descendents of the limited number of library classes, so
> explicitly adding the __metaclass__ attribute to every AppN class is
> awkward.
>
> In fact, in practice, a bunch of AppN classes are likely to be defined
> in yet another support module, call it 'definitions.py'. What I really
> want is to be able to write my application like:
>
> from metaclasses import Meta
> #...something that imposes Meta on all the classes created...
> import library
> from definitions import App1, App2, App3 # Created with Meta
> class App4(library.Base1): ... # also with Meta
> #...more stuff like created instances of AppN...
>
> The person who write the library doesn't know about the definitions.
> But the definitions inherit from some library class. The person who
> writes the definition do not know about the application. But the
> application wants to use the definitions (and the library), but creating
> classes as instances of Meta.
>
> Yours, Lulu...
I wrote the attached program before reading your posting. Maybe a modification
of that can help you. Warning: it is highly untested !!
Here is a short description.
Given a module from the standard distribution (since I had to chose a
module at random to make a first trial, I picked up the random module ;-),
it looks for all its classes and subclasses, then it generates a class
of class containing all these classes, enhanced by a tracing property
induced by a metaclass. In this way, when a method is called, we see the
internal working of the module, i.e. which methods are called and in
which in sequence.
Michele
-----------------------------------------------------------------------------
# metamodule.py
import inspect
class MetaTrace(type):
def __init__(cls,name,bases,dic):
for name in dir(cls):
method=getattr(cls,name)
if inspect.isfunction(method) or inspect.ismethod(method):
setattr(cls,name,cls.wrap(method))
def wrap(cls,method):
def wrapped(*args,**kw):
print "<method=%s> " % method.__name__
ret=method(*args,**kw)
print "</method=%s> " % method.__name__
return ret
return wrapped
class Trace(object):
__metaclass__ = MetaTrace #will be invoked at the creation
#of Trace and children of Trace
#Takes an external module, looks at all its classes, and adds Trace
#capabilities (as a byproduct, classic classic are converted to new style
#classes)
class Module(object): #it is a class of classes but not a metaclass
def __init__(cls,module):
exec 'import '+module+'\n' #I am sure there is a better
moduledict=eval(module+".__dict__") #way to do that
for key,value in moduledict.items():
if inspect.isclass(value):
setattr(cls,key,type(key,(value,Trace),{}))
Random=Module('random') #ATTENTION! No error checking!
#if the module does not exists... #$@!
#to be checked for other modules
r=Random.Random()
print r.random()
#----------------------------------------------------------------------
Output:
<method=__init__>
<method=seed>
</method=seed>
</method=__init__>
<method=random>
</method=random>
0.405580749046
More information about the Python-list
mailing list