author rgbecker
Thu, 26 Nov 2009 15:11:20 +0000
changeset 3268 1ef4fac7831d
parent 3029 eded59f94021
child 3326 ce725978d11c
permissions -rw-r--r-- add advancedUsage=0 argument to AttrMapValue

#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
__version__=''' $Id$ '''
__doc__='''Framework for objects whose assignments are checked. Used by graphics.

We developed reportlab/graphics prior to Python 2 and metaclasses. For the
graphics, we wanted to be able to declare the attributes of a class, check
them on assignment, and convert from string arguments.  Examples of
attrmap-based objects can be found in reportlab/graphics/shapes.  It lets
us defined structures like the one below, which are seen more modern form in
Django models and other frameworks.

We'll probably replace this one day soon, hopefully with no impact on client

class Rect(SolidShape):
    """Rectangle, possibly with rounded corners."""

    _attrMap = AttrMap(BASE=SolidShape,
        x = AttrMapValue(isNumber),
        y = AttrMapValue(isNumber),
        width = AttrMapValue(isNumber),
        height = AttrMapValue(isNumber),
        rx = AttrMapValue(isNumber),
        ry = AttrMapValue(isNumber),

from UserDict import UserDict
from reportlab.lib.validators import isAnything, _SequenceTypes, DerivedValue
from reportlab import rl_config

class CallableValue:
    '''a class to allow callable initial values'''
    def __init__(self,func,*args,**kw):
        #assert iscallable(func)
        self.func = func
        self.args = args = kw

    def __call__(self):
        return apply(self.func,self.args,

class AttrMapValue:
    '''Simple multi-value holder for attribute maps'''
    def __init__(self,validate=None,desc=None,initial=None, advancedUsage=0, **kw):
        self.validate = validate or isAnything
        self.desc = desc
        self._initial = initial
        self._advancedUsage = advancedUsage
        for k,v in kw.items():

    def __getattr__(self,name):
        #hack to allow callable initial values
        if name=='initial':
            if isinstance(self._initial,CallableValue): return self._initial()
            return self._initial
        elif name=='hidden':
            return 0
        raise AttributeError, name

    def __repr__(self):
        return 'AttrMapValue(%s)' % ', '.join(['%s=%r' % i for i in self.__dict__.iteritems()])

class AttrMap(UserDict):
    def __init__(self,BASE=None,UNWANTED=[],**kw):
        data = {}
        if BASE:
            if isinstance(BASE,AttrMap):
                data =                        #they used BASECLASS._attrMap
                if type(BASE) not in (type(()),type([])): BASE = (BASE,)
                for B in BASE:
                    if hasattr(B,'_attrMap'):
                        raise ValueError, 'BASE=%s has wrong kind of value' % str(B)


    def update(self,kw):
        if isinstance(kw,AttrMap): kw =

    def remove(self,unwanted):
        for k in unwanted:
                del self[k]
            except KeyError:

    def clone(self,UNWANTED=[],**kw):
        c = AttrMap(BASE=self,UNWANTED=UNWANTED)
        return c

def validateSetattr(obj,name,value):
    '''validate setattr(obj,name,value)'''
    if rl_config.shapeChecking:
        map = obj._attrMap
        if map and name[0]!= '_':
            #we always allow the inherited values; they cannot
            #be checked until draw time.
            if isinstance(value, DerivedValue):
                #let it through
                    validate = map[name].validate
                    if not validate(value):
                        raise AttributeError, "Illegal assignment of '%s' to '%s' in class %s" % (value, name, obj.__class__.__name__)
                except KeyError:
                    raise AttributeError, "Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__)
    obj.__dict__[name] = value

def _privateAttrMap(obj,ret=0):
    '''clone obj._attrMap if required'''
    A = obj._attrMap
    oA = getattr(obj.__class__,'_attrMap',None)
    if ret:
        if oA is A:
            return A.clone(), oA
            return A, None
        if oA is A:
            obj._attrMap = A.clone()

def _findObjectAndAttr(src, P):
    '''Locate the object src.P for P a string, return parent and name of attribute
    P = string.split(P, '.')
    if len(P) == 0:
        return None, None
        for p in P[0:-1]:
            src = getattr(src, p)
        return src, P[-1]

def hook__setattr__(obj):
    if not hasattr(obj,'__attrproxy__'):
        C = obj.__class__
        import new
            '__setattr__':lambda self,k,v,osa=getattr(obj,'__setattr__',None),hook=hook: hook(self,k,v,osa)})

def addProxyAttribute(src,name,validate=None,desc=None,initial=None,dst=None):
    Add a proxy attribute 'name' to src with targets dst
    assert hasattr(src,'_attrMap'), 'src object has no _attrMap'
    A, oA = _privateAttrMap(src,1)
    if type(dst) not in _SequenceTypes: dst = dst,
    D = []
    DV = []
    for d in dst:
        if type(d) in _SequenceTypes:
            d, e = d[0], d[1:]
        obj, attr = _findObjectAndAttr(src,d)
        if obj:
            dA = getattr(obj,'_attrMap',None)