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