def __init__(self, origin): self.origin = origin self.dct = dict( __metachao_aspects__=getattr(origin, '__metachao_aspects__', [])[:] ) if utils.isclass(origin): # Aspect application does not change the name. This can # lead to messages like "... expects a.A not a.A". self.name = origin.__name__ self.baseclasses = (origin,) self.type = type(origin) # Aspect application does not change the module. If that # is not what you want, consider subclassing first. # XXX: it totally should indicate that sth is different #self.dct['__module__'] = origin.__module__ self.dct['__doc__'] = origin.__doc__ else: # we are pretty much creating an object that uses origin # as prototype. self.name = "Prototyper:%s" % (origin.__class__.__name__,) self.baseclasses = () self.type = type # bound methods found on origin, except if blacklisted blacklist = ( '__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metachao_origin__', '__metachao_prototype__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', ) self.dct.update(((k, getattr(origin, k)) for k, v in getmembers(origin) if callable(v) and not k in blacklist)) # properties bound to origin for all properties found on # origin's class self.dct.update(((k, prototype_property(origin, v)) for k, v in getmembers(origin.__class__) if type(v) is property)) # getattr fallback to origin, setattr and delattr on new self.dct['__getattr__'] = lambda _, name: getattr(origin, name) # empty __init__ needed if a later aspect plumbs it self.dct['__init__'] = lambda *a, **kw: None self.dct['__metachao_prototype__'] = origin self.dct['__doc__'] = origin.__doc__ if '__metachao_effective__' in getattr(origin, '__dict__', ()): self.dct['__metachao_effective__'] = \ origin.__metachao_effective__.copy()
def __call__(aspect, origin=_UNSET, **kw): if kw.pop('pdb', None): import pdb pdb.set_trace() elif kw.pop('ipdb', None): import ipdb ipdb.set_trace() if kw: aspect = configured_aspect(aspect, kw) if origin is _UNSET: return aspect if origin is None: raise ValueError("Need aspect, class, or instance, not %r!" % (origin,)) # if called with another aspect compose them if type(origin) is AspectMeta: return compose(aspect, origin) # a single aspects called on a normal class or an instance workbench = Workbench(origin) for instruction in aspect.__metachao_instructions__.values(): instruction(workbench) # build a new class, with the same name and bases as the # target class, but a new dictionary with the aspect applied. # XXX: name is length limited... hackup traceback or something # to generate more useful info #name = '%s:%s' % (aspect.__name__, workbench.name) name = workbench.name workbench.dct['__metachao_aspects__'].insert(0, aspect) cls = workbench.type(name, workbench.baseclasses, workbench.dct) if ZOPE_INTERFACE_AVAILABLE: classImplements(cls, *tuple(implementedBy(aspect))) if utils.isclass(origin): cls.__metachao_class__ = cls return cls return cls()
def __call__(aspect, origin=_UNSET, **kw): if kw.get('pdb'): import pdb;pdb.set_trace() elif kw.get('ipdb'): import ipdb;ipdb.set_trace() # if called without positional arg, return partially applied # aspect if origin is _UNSET: if not kw: raise NeedKw # XXX: this does not play nice with ABC # XXX: return an aspect that is differently configured return Partial(aspect, **kw) if origin is None: raise ValueError( "Need aspect, class, or instance, not %r!" % (origin,)) # if called with another aspect compose them if type(origin) is AspectMeta: if kw: raise Unsupported("kw and composition not supported") name = "AspectComposition" aspects = [] for asp in (aspect, origin): if hasattr(asp, '__metachao_compose__'): aspects.extend(asp.__metachao_compose__) else: aspects.append(asp) composite = AspectMeta(name, (Aspect,), dict(__metachao_compose__=aspects)) type(origin).register(origin, composite) type(aspect).register(aspect, composite) return composite # if composition, chain them if hasattr(aspect, '__metachao_compose__'): for asp in reversed(aspect.__metachao_compose__): origin = asp(origin, **kw) if type(origin) is type: type(aspect).register(aspect, origin) return origin # a single aspects called on a normal class or an instance workbench = Workbench(origin, **kw) instrs = Instructions(aspect) instrs(workbench) #raise AspectCollision(instr.name, aspect, target) # in case of instances functions need to be bound # if not x_is_class and (type(instr) is types.FunctionType): # instr = instr.__get__(x) # build a new class, with the same name and bases as the # target class, but a new dictionary with the aspect applied. # XXX: name is lenght limited... hackup traceback or something # to generate more useful info #name = '%s:%s' % (aspect.__name__, workbench.name) name = workbench.name cls = workbench.type(name, workbench.baseclasses, workbench.dct) type(aspect).register(aspect, cls) if ZOPE_INTERFACE_AVAILABLE: classImplements(cls, *tuple(implementedBy(aspect))) if utils.isclass(origin): if type(cls) is AspectMeta and kw: raise OldCodePath return Partial(cls, **kw) cls.__metachao_class__ = cls return cls return cls()