faqts : Computers : Programming : Languages : Python : Snippets

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

3 of 5 people (60%) answered Yes
Recently 1 of 3 people (33%) answered Yes

Entry

Behavior injection

Jul 5th, 2000 10:03
Nathan Wallace, Hans Nowak, Snippet 305, Fred Gansevles


"""
Packages: arcane_arts
"""
""" Attr.py -- Attribute management
by Behaviour Injection
    Version     0.1
    Date        Mon Dec  7, 1998
    Author      F. Gansevles <[email protected]>
"""
class __:
    """ __ - the general '__XXXattr__' class
        The instance of this class registers itself in the name-space of
        the caller as '__self'. It also injects the 'Class'-class into the
        class-chain of the original instance.
        The attributes used here are:
            attrs        The attribute-cache used by the AttributeClasses
                        to store the names or name-values of the
                        attributes (i.e a bacing-store for __dict__)
            self        The 'original' self. This is the instance that let
                        the AttributeClass *inject* itself into the
                        class-chain
            base        The 'original' base-class of the injected
            instance.
        The methods defined are:
            setattr     Propagate the __setattr__ call
            getattr     Propagate the __getattr__ call
            delattr     Propagate the __delattr__ call
    """
    def __init__ (__, Class, self, attrs):
        # __ is an instance of __ (It's confusing, I know.)
        name = '_%s__self' % Class.__name__     # create '__self'
        if self.__dict__.has_key (name):        # re-use the __self instance
            self.__dict__[name].__update (attrs)
        else:
            __.attrs = attrs                    # The attributes this whole
                                                # thing is about.
            __.base = self.__class__            # Keep the original class around
                                                # so attribute-lookup can
                                                # continue 'up' the chain.
            __.self = self                      # This instance gets injected. 
            self.__dict__[name] = __            # Register as '__self' 
            self.__class__ = Class              # Now 'self' is an instance of
                                                # 'Class'
    def __update (__, attrs):
        # Private method
        # merge the new attrs with the existing ones
        if type (__.attrs) == type ({}):
            __.attrs.update (attrs)
        else:
            __.attrs = __.attrs + attrs
    def setattr (__, attr, val):
        # look-up the chain for __setattr__
        if hasattr (__.base, '__setattr__'):
            # Ok, apply it
            return __.base.__setattr__ (__.self, attr, val)
        # not found, default behaviour
        __.self.__dict__[attr] = val
    def getattr (__, attr):
        try:
            # get the attribute
            return __.self.__dict__[attr]
        except KeyError:
            # look-up the chain for __getattr__
            if hasattr (__.base, '__getattr__'):
                return __.base.__getattr__ (__.self, attr)
        # not found, default behaviour
        raise AttributeError, attr
    def delattr (__, attr):
        # look-up the chain for __delattr__
        if hasattr (__.base, '__delattr__'):
            return __.base.__delattr__ (__.self, attr)
        # not found, default behaviour
        try:
            del __.self.__dict__[attr]
        except KeyError:
            raise AttributeError, 'delete non-existing instance attribute'
def Attribute (self, *attrs):
    """ Only attributes listed in 'attrs' may be modified
    """
    class Attribute (self.__class__):
        def __setattr__ (self, attr, val):
            if not attr in self.__self.attrs:
                raise AttributeError, "Undeclared attribute: %s" % `attr`
            self.__self.setattr (attr, val)
    # inject 'Attribute' into the class-chain
    __ (Attribute, self, attrs)
def Persistent (self, *attrs):
    """ All attributes listed in 'attrs' may *not* be removed
    """
    class Persistent (self.__class__):
        def __delattr__ (self, attr):
            if attr in self.__self.attrs:
                if hasattr (self, attr):
                    raise AttributeError, "Persistent attribute: %s" % `attr`
            self.__self.delattr (attr)
    __ (Persistent, self, attrs)
def Readonly (self, *attrs):
    """ All attributes listed in 'attrs' may *not* be changed or removed
    """ 
    class Readonly (self.__class__):
        def __setattr__ (self, attr, val):
            if attr in self.__self.attrs:
                if hasattr (self, attr):
                    raise AttributeError, "Readonly attribute: %s" % `attr`
            self.__self.setattr (attr, val)
        def __delattr__ (self, attr):
            if attr in self.__self.attrs:
                if hasattr (self, attr):
                    raise AttributeError, "Readonly attribute: %s" % `attr`
            self.__self.delattr (attr)
    # inject 'Readonly' into the class-chain
    __ (Readonly, self, attrs)
    # instead of defining a __delattr__ method you could also add
    # a call to Persistent, i.e. 'apply (Persistent, (self,)+ attrs)'
def Typed (self, *attrs, **types):
    """ All attributes listed in 'attrs' must be of the given type
    """
    class Typed (self.__class__):
        def __setattr__ (self, attr, val):
            if self.__self.attrs.has_key (attr):
                if self.__self.attrs[attr] <> type (val):
                    raise TypeError, "Type mismatch for %s, %s expected "\
                    % (`attr`, self.__self.attrs[attr])
            self.__self.setattr (attr, val)
    for attr in attrs:
        if hasattr (self, attr):
            types[attr] = type (getattr (self, attr))
    __ (Typed, self, types)
def Doc (self, **attrs):
    """ Add a hidden '__doc' extension to the attributes listed in 'attrs'
    """ 
    class Doc (self.__class__):
        def __getattr__ (self, attr):
            if attr[-5:] == '__doc':
                doc = attr[:-5]
                try:
                    self.__self.getattr (doc)
                except AttributeError:
                    return self.__self.getattr (attr)
                try:
                    return self.__self.attrs [doc]
                except KeyError:
                    return None
            return self.__self.getattr (attr)
    __ (Doc, self, attrs)
class Class:
    """ A test-class for Attr.py
    """
    __dict = None
    def __init__ (self):
        # I use a 'hidden' dictionary to show that, in the end, the
        # Class.__XXXattr__ methods are called.
        self.__dict = {}
        Attribute       (self,  'a', 'b', 'c')
        Readonly        (self,  'a')
        Typed           (self,  'a', b = type(''))
        Persistent      (self,  'b')
        Doc             (self,  a = 'This is a read-only attribute')
        self.a = 1
        self.b = '2'
    def __setattr__ (self, attr, val):
        if self.__dict is None:
            self.__dict__[attr] = val
        else:
            print 'Class::%s = %s' % (attr, val)
            self.__dict[attr] = val
    def __getattr__ (self, attr):
        try:
            val = self.__dict[attr]
            print 'Class::%s --> %s' % (attr, val)
            return val
        except KeyError:
            raise AttributeError, attr
    def __delattr__ (self, attr):
        try:
            del self.__dict[attr]
            print 'del Class::%s' % attr
        except KeyError:
            raise AttributeError, attr
c = Class ()
# try this as follows:
# python -i Attrs.py
# >>> c.x = 1           # Attribute
# >>> c.a = 2           # Readonly
# >>> c.b = 2           # Typed
# >>> del c.b           # Persistent
# >>> print c.a__doc    # Doc
if __name__ == "__main__":
    import sys
    try: c.x = 1
    except: print sys.exc_value
    try: c.a = 2
    except: print sys.exc_value
    try: c.b = 2
    except: print sys.exc_value
    c.b = "Hello"
    try: del c.b
    except: print sys.exc_value
    print c.a__doc, c.b__doc