Esempio n. 1
0
    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()
Esempio n. 2
0
    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()
Esempio n. 3
0
    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()