goulash.metaclasses module
goulash.metaclasses
random experiments with metaclasses.
you probably don't want to use this stuff :)
""" goulash.metaclasses
random experiments with metaclasses.
you probably don't want to use this stuff :)
"""
import new, copy
from collections import defaultdict
from goulash.util import uniq
subclass_registry = defaultdict(lambda:[])
def metaclass_hook(func):
func.metaclass_hook = True
return staticmethod(func)
def dynamic_name(): return 'DynMix({U})'.format(U=uniq())
class META(type):
""" the most generic metaclass.
all this does is provide support for hooks. by default the
class-registration-hook is turned on, but if you want that
turned off just subclass this and set `metaclass_hooks=[]`
NB: to avoid MRO issues, this should be the main metaclass
that's used, and everything else should subclass it.
metaclass_registration_hook::
this tracks class-hierarchy information for every class
that uses this metaclass. it keeps a dictionary of this
form updated:
subclass_registry[<__bases__ list>] = [, ..]
"""
@staticmethod
def enumerate_metaclass_hooks(mcls):
""" returns a dictionary of metaclass hooks
that will be run along with __new___
"""
# TODO: use Namespace()
matches = [ x for x in dir(mcls) if \
getattr(getattr(mcls, x, None),
'metaclass_hook', False) ]
return dict([[match, getattr(mcls, match)] for match in matches])
@metaclass_hook
def metaclass_class_registration_hook(mcls, name, bases, dct, class_obj):
""" called when initializing (configuring) class,
this method records data about hierarchy structure
"""
subclass_registry[bases].append(class_obj)
def __new__(mcls, name, bases, dct):
""" simply reproduce the usual behaviour of type.__new__
run any hooks (hooks are defined by subclassers)
"""
try:
class_obj = type.__new__(mcls, name, bases, dct)
except TypeError,e:
# probably the cannot create consistent MRO error
print dict([[b.__name__,
getattr(b, '__metaclass__',None)] for b in bases])
raise e
hooks = mcls.metaclass_hooks if hasattr(mcls, 'metaclass_hooks') else \
mcls.enumerate_metaclass_hooks(mcls)
for hook in hooks.values():
hook(mcls, name, bases, dct, class_obj)
return class_obj
class ClassAlgebra(META):
""" a metaclass that tracks it's subclasses. """
def __lshift__(kls, my_mixin):
""" algebra for left-mixin
The following are equivalent:
>>> my_class = my_class << my_mixin
>>> class my_class(my_mixin, my_class): pass
"""
name = dynamic_name()
bases = (my_mixin, kls)
return kls.__metaclass__(name, bases, {})
def __rshift__(kls, my_mixin):
""" algebra for right-mixin:
The following are equivalent:
>>> my_class = my_class >> my_mixin
>>> class my_class(my_class,my_mixin): pass
"""
name = dynamic_name()
bases = (kls, my_mixin)
return kls.__metaclass__(name, bases, {})
def subclass(kls, name=None, dct={}, **kargs):
""" dynamically generate a subclass of this class """
dct = copy.copy(dct)
dct.update(kargs)
if hasattr(kls, '_subclass_hooks'):
# TODO: shouldnt be here, abstract this
name, dct = kls._subclass_hooks(name=name, **dct)
name = name or "DynamicSubclassOf{K}_{U}".format(K=kls.__name__,
U=uniq())
# why does this behave differently than type() ?
return new.classobj(name, (kls,), dct)
META1 = ClassAlgebra
def supports_class_algebra(kls):
""" for use as a decorator
"""
if hasattr(kls,'__metaclass__'):
if kls.__metaclass__!=ClassAlgebra:
raise TypeError("{0} already has a metaclass: '{1}'".
format(kls, kls.__metaclass__))
else:
return kls
else:
class Temp(kls):
__metaclass__ = ClassAlgebra
Temp.__name__ = kls.__name__
return Temp
def subclass_tracker(*bases, **kargs):
""" dynamically generates the subclass tracking class that extends ``bases``.
often the name doesn't matter and will never be seen,
but you might as well be verbose in case it's stumbled across.
usually an empty dictionary is fine for the namespace.. after all you're
specifying the bases already, right?
Example usage follows:
SomeService(classtracker(Service, Mixin1, Mixin2)):
''' function body '''
"""
if kargs:
assert kargs.keys() == ['namespace'],'only the namespace kw arg is defined'
namespace = kargs.pop('namespace')
else:
namespace = {}
name = 'DynamicallyGeneratedClassTracker'
return META(name, bases, namespace)
Module variables
var subclass_registry
Functions
def dynamic_name(
)
def dynamic_name(): return 'DynMix({U})'.format(U=uniq())
def metaclass_hook(
func)
def metaclass_hook(func):
func.metaclass_hook = True
return staticmethod(func)
def subclass_tracker(
*bases, **kargs)
dynamically generates the subclass tracking class that extends bases
.
often the name doesn't matter and will never be seen, but you might as well be verbose in case it's stumbled across.
usually an empty dictionary is fine for the namespace.. after all you're specifying the bases already, right?
Example usage follows:
SomeService(classtracker(Service, Mixin1, Mixin2)): ''' function body '''
def subclass_tracker(*bases, **kargs):
""" dynamically generates the subclass tracking class that extends ``bases``.
often the name doesn't matter and will never be seen,
but you might as well be verbose in case it's stumbled across.
usually an empty dictionary is fine for the namespace.. after all you're
specifying the bases already, right?
Example usage follows:
SomeService(classtracker(Service, Mixin1, Mixin2)):
''' function body '''
"""
if kargs:
assert kargs.keys() == ['namespace'],'only the namespace kw arg is defined'
namespace = kargs.pop('namespace')
else:
namespace = {}
name = 'DynamicallyGeneratedClassTracker'
return META(name, bases, namespace)
def supports_class_algebra(
kls)
for use as a decorator
def supports_class_algebra(kls):
""" for use as a decorator
"""
if hasattr(kls,'__metaclass__'):
if kls.__metaclass__!=ClassAlgebra:
raise TypeError("{0} already has a metaclass: '{1}'".
format(kls, kls.__metaclass__))
else:
return kls
else:
class Temp(kls):
__metaclass__ = ClassAlgebra
Temp.__name__ = kls.__name__
return Temp
Classes
class ClassAlgebra
a metaclass that tracks it's subclasses.
class ClassAlgebra(META):
""" a metaclass that tracks it's subclasses. """
def __lshift__(kls, my_mixin):
""" algebra for left-mixin
The following are equivalent:
>>> my_class = my_class << my_mixin
>>> class my_class(my_mixin, my_class): pass
"""
name = dynamic_name()
bases = (my_mixin, kls)
return kls.__metaclass__(name, bases, {})
def __rshift__(kls, my_mixin):
""" algebra for right-mixin:
The following are equivalent:
>>> my_class = my_class >> my_mixin
>>> class my_class(my_class,my_mixin): pass
"""
name = dynamic_name()
bases = (kls, my_mixin)
return kls.__metaclass__(name, bases, {})
def subclass(kls, name=None, dct={}, **kargs):
""" dynamically generate a subclass of this class """
dct = copy.copy(dct)
dct.update(kargs)
if hasattr(kls, '_subclass_hooks'):
# TODO: shouldnt be here, abstract this
name, dct = kls._subclass_hooks(name=name, **dct)
name = name or "DynamicSubclassOf{K}_{U}".format(K=kls.__name__,
U=uniq())
# why does this behave differently than type() ?
return new.classobj(name, (kls,), dct)
Ancestors (in MRO)
- ClassAlgebra
- META
- __builtin__.type
- __builtin__.object
Static methods
def enumerate_metaclass_hooks(
mcls)
Inheritance:
META
.enumerate_metaclass_hooks
returns a dictionary of metaclass hooks that will be run along with new_
@staticmethod
def enumerate_metaclass_hooks(mcls):
""" returns a dictionary of metaclass hooks
that will be run along with __new___
"""
# TODO: use Namespace()
matches = [ x for x in dir(mcls) if \
getattr(getattr(mcls, x, None),
'metaclass_hook', False) ]
return dict([[match, getattr(mcls, match)] for match in matches])
def metaclass_class_registration_hook(
mcls, name, bases, dct, class_obj)
Inheritance:
META
.metaclass_class_registration_hook
called when initializing (configuring) class, this method records data about hierarchy structure
@metaclass_hook
def metaclass_class_registration_hook(mcls, name, bases, dct, class_obj):
""" called when initializing (configuring) class,
this method records data about hierarchy structure
"""
subclass_registry[bases].append(class_obj)
Methods
def subclass(
kls, name=None, dct={}, **kargs)
dynamically generate a subclass of this class
def subclass(kls, name=None, dct={}, **kargs):
""" dynamically generate a subclass of this class """
dct = copy.copy(dct)
dct.update(kargs)
if hasattr(kls, '_subclass_hooks'):
# TODO: shouldnt be here, abstract this
name, dct = kls._subclass_hooks(name=name, **dct)
name = name or "DynamicSubclassOf{K}_{U}".format(K=kls.__name__,
U=uniq())
# why does this behave differently than type() ?
return new.classobj(name, (kls,), dct)
class META
the most generic metaclass.
all this does is provide support for hooks. by default the
class-registration-hook is turned on, but if you want that
turned off just subclass this and set metaclass_hooks=[]
NB: to avoid MRO issues, this should be the main metaclass that's used, and everything else should subclass it.
metaclass_registration_hook:: this tracks class-hierarchy information for every class that uses this metaclass. it keeps a dictionary of this form updated:
subclass_registry[<__bases__ list>] = [<subclass1>, ..]
class META(type):
""" the most generic metaclass.
all this does is provide support for hooks. by default the
class-registration-hook is turned on, but if you want that
turned off just subclass this and set `metaclass_hooks=[]`
NB: to avoid MRO issues, this should be the main metaclass
that's used, and everything else should subclass it.
metaclass_registration_hook::
this tracks class-hierarchy information for every class
that uses this metaclass. it keeps a dictionary of this
form updated:
subclass_registry[<__bases__ list>] = [, ..]
"""
@staticmethod
def enumerate_metaclass_hooks(mcls):
""" returns a dictionary of metaclass hooks
that will be run along with __new___
"""
# TODO: use Namespace()
matches = [ x for x in dir(mcls) if \
getattr(getattr(mcls, x, None),
'metaclass_hook', False) ]
return dict([[match, getattr(mcls, match)] for match in matches])
@metaclass_hook
def metaclass_class_registration_hook(mcls, name, bases, dct, class_obj):
""" called when initializing (configuring) class,
this method records data about hierarchy structure
"""
subclass_registry[bases].append(class_obj)
def __new__(mcls, name, bases, dct):
""" simply reproduce the usual behaviour of type.__new__
run any hooks (hooks are defined by subclassers)
"""
try:
class_obj = type.__new__(mcls, name, bases, dct)
except TypeError,e:
# probably the cannot create consistent MRO error
print dict([[b.__name__,
getattr(b, '__metaclass__',None)] for b in bases])
raise e
hooks = mcls.metaclass_hooks if hasattr(mcls, 'metaclass_hooks') else \
mcls.enumerate_metaclass_hooks(mcls)
for hook in hooks.values():
hook(mcls, name, bases, dct, class_obj)
return class_obj
Ancestors (in MRO)
- META
- __builtin__.type
- __builtin__.object
Static methods
def enumerate_metaclass_hooks(
mcls)
returns a dictionary of metaclass hooks that will be run along with new_
@staticmethod
def enumerate_metaclass_hooks(mcls):
""" returns a dictionary of metaclass hooks
that will be run along with __new___
"""
# TODO: use Namespace()
matches = [ x for x in dir(mcls) if \
getattr(getattr(mcls, x, None),
'metaclass_hook', False) ]
return dict([[match, getattr(mcls, match)] for match in matches])
def metaclass_class_registration_hook(
mcls, name, bases, dct, class_obj)
called when initializing (configuring) class, this method records data about hierarchy structure
@metaclass_hook
def metaclass_class_registration_hook(mcls, name, bases, dct, class_obj):
""" called when initializing (configuring) class,
this method records data about hierarchy structure
"""
subclass_registry[bases].append(class_obj)
class META1
a metaclass that tracks it's subclasses.
class ClassAlgebra(META):
""" a metaclass that tracks it's subclasses. """
def __lshift__(kls, my_mixin):
""" algebra for left-mixin
The following are equivalent:
>>> my_class = my_class << my_mixin
>>> class my_class(my_mixin, my_class): pass
"""
name = dynamic_name()
bases = (my_mixin, kls)
return kls.__metaclass__(name, bases, {})
def __rshift__(kls, my_mixin):
""" algebra for right-mixin:
The following are equivalent:
>>> my_class = my_class >> my_mixin
>>> class my_class(my_class,my_mixin): pass
"""
name = dynamic_name()
bases = (kls, my_mixin)
return kls.__metaclass__(name, bases, {})
def subclass(kls, name=None, dct={}, **kargs):
""" dynamically generate a subclass of this class """
dct = copy.copy(dct)
dct.update(kargs)
if hasattr(kls, '_subclass_hooks'):
# TODO: shouldnt be here, abstract this
name, dct = kls._subclass_hooks(name=name, **dct)
name = name or "DynamicSubclassOf{K}_{U}".format(K=kls.__name__,
U=uniq())
# why does this behave differently than type() ?
return new.classobj(name, (kls,), dct)
Ancestors (in MRO)
- ClassAlgebra
- META
- __builtin__.type
- __builtin__.object
Static methods
def enumerate_metaclass_hooks(
mcls)
Inheritance:
ClassAlgebra
.enumerate_metaclass_hooks
returns a dictionary of metaclass hooks that will be run along with new_
@staticmethod
def enumerate_metaclass_hooks(mcls):
""" returns a dictionary of metaclass hooks
that will be run along with __new___
"""
# TODO: use Namespace()
matches = [ x for x in dir(mcls) if \
getattr(getattr(mcls, x, None),
'metaclass_hook', False) ]
return dict([[match, getattr(mcls, match)] for match in matches])
def metaclass_class_registration_hook(
mcls, name, bases, dct, class_obj)
Inheritance:
ClassAlgebra
.metaclass_class_registration_hook
called when initializing (configuring) class, this method records data about hierarchy structure
@metaclass_hook
def metaclass_class_registration_hook(mcls, name, bases, dct, class_obj):
""" called when initializing (configuring) class,
this method records data about hierarchy structure
"""
subclass_registry[bases].append(class_obj)
Methods
def subclass(
kls, name=None, dct={}, **kargs)
Inheritance:
ClassAlgebra
.subclass
dynamically generate a subclass of this class
def subclass(kls, name=None, dct={}, **kargs):
""" dynamically generate a subclass of this class """
dct = copy.copy(dct)
dct.update(kargs)
if hasattr(kls, '_subclass_hooks'):
# TODO: shouldnt be here, abstract this
name, dct = kls._subclass_hooks(name=name, **dct)
name = name or "DynamicSubclassOf{K}_{U}".format(K=kls.__name__,
U=uniq())
# why does this behave differently than type() ?
return new.classobj(name, (kls,), dct)