Welcome, guest | Sign In | My Account | Store | Cart
import dis, new

def _pop_funclist(f):
    """_pop_funclist(f) -> list or None

    Evaluates and returns a list constant defined at the beginning
    of a function. If the function doesn't begin with a list,
    or the list refers to parameters or other locals, a None is returned.
    The returned list is removed from the function code.
    """
    op = dis.opmap.__getitem__
    i = 0
    co = f.func_code
    s = co.co_code
    stopcodes = [op('LOAD_FAST'), op('STORE_FAST'), op('STORE_NAME'),
                  op('POP_TOP'), op('JUMP_FORWARD')]
    while i < len(s):
        code = ord(s[i])
        i += 1
        if code >= dis.HAVE_ARGUMENT:
            i += 2
        if code in stopcodes:
            return
        if code == op('BUILD_LIST') and ord(s[i]) == op('POP_TOP'):
            i += 1
            break
    else:
        return
    varname = '__func_list__'
    names = co.co_names + (varname,)
    dict_code = co.co_code[:i-1] + ''.join(map(chr, [
        op('STORE_NAME'),
        list(names).index(varname), 0,        
        op('LOAD_CONST'),
        list(co.co_consts).index(None), 0,
        op('RETURN_VALUE'),
        ]))
    func_code = chr(op('JUMP_FORWARD')) \
                + chr(i-3) + chr(0) \
                + co.co_code[3:]
    list_co = new.code(0, 0, co.co_stacksize, 64, dict_code,
                       co.co_consts, names, co.co_varnames, co.co_filename,
                       co.co_name, co.co_firstlineno, co.co_lnotab)
    func_co = new.code(co.co_argcount, co.co_nlocals, co.co_stacksize, co.co_flags, func_code,
                       co.co_consts, co.co_names, co.co_varnames, co.co_filename,
                       co.co_name, co.co_firstlineno, co.co_lnotab)
    f.func_code = func_co
    globals = f.func_globals.copy()
    exec list_co in globals
    result = globals[varname]
    return result
    
def decorate(func):
    """decorate(func) -> func

    Gets the decorator list from a function and if found,
    applies the decorators in order and returns the transformed
    function.
    """    
    funclist = _pop_funclist(func)
    if funclist is None: return func
    f = func
    for d in funclist:
        if callable(d):
            f = d(f)
    return f
    

def attrs(**kw):
    def setattrs(f):
        f.__dict__.update(kw)
        return f
    return setattrs

class list_decorators(type):
    """
    Metaclass that calls the decorate function for all
    methods defined in the class.
    """
    def __init__(cls, name, bases, dct):
        type.__init__(cls, name, bases, dct)
        for k,v in dct.iteritems():
            if hasattr(v, "func_code"):
                setattr(cls, k, decorate(v))
    
class TestClass(object):
    __metaclass__ = list_decorators

    def normal(self):
        self.x = 10

    def static(x):
        [attrs(author="shang", version=2),
         staticmethod]
        print x

    def name(cls):
        [classmethod]
        return cls.__name__


>>> TestClass.static.author
'shang'
>>> TestClass.static.version
2
>>> TestClass.name()
'TestClass'

History

  • revision 2 (18 years ago)
  • previous revisions are not available