src/reportlab/lib/attrmap.py
author rptlab
Tue, 30 Apr 2013 14:28:14 +0100
branchpy33
changeset 3723 99aa837b6703
parent 3721 0c93dd8ff567
child 3762 4817e577522d
permissions -rw-r--r--
second stage of port to Python 3.3; working hello world
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
3617
ae5744e97c42 reportlab: copyright date changes
robin
parents: 3326
diff changeset
     1
#Copyright ReportLab Europe Ltd. 2000-2012
946
0bda695b09ce Initial version
rgbecker
parents:
diff changeset
     2
#see license.txt for license details
2332
2a7ab4405e18 Remove $Header:, fix CopyRight & history
rgbecker
parents: 2027
diff changeset
     3
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/attrmap.py
2a7ab4405e18 Remove $Header:, fix CopyRight & history
rgbecker
parents: 2027
diff changeset
     4
__version__=''' $Id$ '''
3029
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
     5
__doc__='''Framework for objects whose assignments are checked. Used by graphics.
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
     6
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
     7
We developed reportlab/graphics prior to Python 2 and metaclasses. For the
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
     8
graphics, we wanted to be able to declare the attributes of a class, check
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
     9
them on assignment, and convert from string arguments.  Examples of
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    10
attrmap-based objects can be found in reportlab/graphics/shapes.  It lets
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    11
us defined structures like the one below, which are seen more modern form in
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    12
Django models and other frameworks.
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    13
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    14
We'll probably replace this one day soon, hopefully with no impact on client
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    15
code.
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    16
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    17
class Rect(SolidShape):
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    18
    """Rectangle, possibly with rounded corners."""
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    19
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    20
    _attrMap = AttrMap(BASE=SolidShape,
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    21
        x = AttrMapValue(isNumber),
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    22
        y = AttrMapValue(isNumber),
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    23
        width = AttrMapValue(isNumber),
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    24
        height = AttrMapValue(isNumber),
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    25
        rx = AttrMapValue(isNumber),
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    26
        ry = AttrMapValue(isNumber),
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    27
        )
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    28
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    29
eded59f94021 adding docstrings to lib
andy
parents: 2964
diff changeset
    30
'''
2547
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
    31
from reportlab.lib.validators import isAnything, _SequenceTypes, DerivedValue
2027
6b47f09c8d1e Add check for shapeChecking to validateSetattr
rgbecker
parents: 2025
diff changeset
    32
from reportlab import rl_config
946
0bda695b09ce Initial version
rgbecker
parents:
diff changeset
    33
0bda695b09ce Initial version
rgbecker
parents:
diff changeset
    34
class CallableValue:
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    35
    '''a class to allow callable initial values'''
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    36
    def __init__(self,func,*args,**kw):
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    37
        #assert iscallable(func)
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    38
        self.func = func
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    39
        self.args = args
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    40
        self.kw = kw
946
0bda695b09ce Initial version
rgbecker
parents:
diff changeset
    41
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    42
    def __call__(self):
3326
ce725978d11c Initial Python3 compatibility fixes
damian
parents: 3268
diff changeset
    43
        return self.func(*self.args,**self.kw)
1683
7fa753e4420a Removed all trailing whitespace
andy_robinson
parents: 1677
diff changeset
    44
946
0bda695b09ce Initial version
rgbecker
parents:
diff changeset
    45
class AttrMapValue:
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    46
    '''Simple multi-value holder for attribute maps'''
3268
1ef4fac7831d attmap.py: add advancedUsage=0 argument to AttrMapValue
rgbecker
parents: 3029
diff changeset
    47
    def __init__(self,validate=None,desc=None,initial=None, advancedUsage=0, **kw):
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    48
        self.validate = validate or isAnything
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    49
        self.desc = desc
2489
f0560094aae9 attrmap: fix initialization of _initial
rgbecker
parents: 2332
diff changeset
    50
        self._initial = initial
3268
1ef4fac7831d attmap.py: add advancedUsage=0 argument to AttrMapValue
rgbecker
parents: 3029
diff changeset
    51
        self._advancedUsage = advancedUsage
3723
99aa837b6703 second stage of port to Python 3.3; working hello world
rptlab
parents: 3721
diff changeset
    52
        for k,v in kw.items():
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    53
            setattr(self,k,v)
946
0bda695b09ce Initial version
rgbecker
parents:
diff changeset
    54
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    55
    def __getattr__(self,name):
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    56
        #hack to allow callable initial values
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    57
        if name=='initial':
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    58
            if isinstance(self._initial,CallableValue): return self._initial()
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    59
            return self._initial
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    60
        elif name=='hidden':
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    61
            return 0
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
    62
        raise AttributeError(name)
946
0bda695b09ce Initial version
rgbecker
parents:
diff changeset
    63
3268
1ef4fac7831d attmap.py: add advancedUsage=0 argument to AttrMapValue
rgbecker
parents: 3029
diff changeset
    64
    def __repr__(self):
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
    65
        return 'AttrMapValue(%s)' % ', '.join(['%s=%r' % i for i in self.__dict__.items()])
3268
1ef4fac7831d attmap.py: add advancedUsage=0 argument to AttrMapValue
rgbecker
parents: 3029
diff changeset
    66
3723
99aa837b6703 second stage of port to Python 3.3; working hello world
rptlab
parents: 3721
diff changeset
    67
class AttrMap(dict):
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    68
    def __init__(self,BASE=None,UNWANTED=[],**kw):
1693
1a71e3509e39 Allow for multiple BASEs
rgbecker
parents: 1683
diff changeset
    69
        data = {}
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    70
        if BASE:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    71
            if isinstance(BASE,AttrMap):
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    72
                data = BASE.data                        #they used BASECLASS._attrMap
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    73
            else:
1693
1a71e3509e39 Allow for multiple BASEs
rgbecker
parents: 1683
diff changeset
    74
                if type(BASE) not in (type(()),type([])): BASE = (BASE,)
1a71e3509e39 Allow for multiple BASEs
rgbecker
parents: 1683
diff changeset
    75
                for B in BASE:
1a71e3509e39 Allow for multiple BASEs
rgbecker
parents: 1683
diff changeset
    76
                    if hasattr(B,'_attrMap'):
1a71e3509e39 Allow for multiple BASEs
rgbecker
parents: 1683
diff changeset
    77
                        data.update(getattr(B._attrMap,'data',{}))
1a71e3509e39 Allow for multiple BASEs
rgbecker
parents: 1683
diff changeset
    78
                    else:
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
    79
                        raise ValueError('BASE=%s has wrong kind of value' % str(B))
946
0bda695b09ce Initial version
rgbecker
parents:
diff changeset
    80
3723
99aa837b6703 second stage of port to Python 3.3; working hello world
rptlab
parents: 3721
diff changeset
    81
        dict.__init__(self,data)
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    82
        self.remove(UNWANTED)
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    83
        self.data.update(kw)
948
2531b35adf85 New AttrMap emplacement
rgbecker
parents: 946
diff changeset
    84
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    85
    def update(self,kw):
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    86
        if isinstance(kw,AttrMap): kw = kw.data
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    87
        self.data.update(kw)
946
0bda695b09ce Initial version
rgbecker
parents:
diff changeset
    88
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    89
    def remove(self,unwanted):
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    90
        for k in unwanted:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    91
            try:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    92
                del self[k]
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    93
            except KeyError:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    94
                pass
946
0bda695b09ce Initial version
rgbecker
parents:
diff changeset
    95
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    96
    def clone(self,UNWANTED=[],**kw):
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    97
        c = AttrMap(BASE=self,UNWANTED=UNWANTED)
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    98
        c.update(kw)
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
    99
        return c
948
2531b35adf85 New AttrMap emplacement
rgbecker
parents: 946
diff changeset
   100
2531b35adf85 New AttrMap emplacement
rgbecker
parents: 946
diff changeset
   101
def validateSetattr(obj,name,value):
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   102
    '''validate setattr(obj,name,value)'''
2027
6b47f09c8d1e Add check for shapeChecking to validateSetattr
rgbecker
parents: 2025
diff changeset
   103
    if rl_config.shapeChecking:
6b47f09c8d1e Add check for shapeChecking to validateSetattr
rgbecker
parents: 2025
diff changeset
   104
        map = obj._attrMap
6b47f09c8d1e Add check for shapeChecking to validateSetattr
rgbecker
parents: 2025
diff changeset
   105
        if map and name[0]!= '_':
2547
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
   106
            #we always allow the inherited values; they cannot
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
   107
            #be checked until draw time.
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
   108
            if isinstance(value, DerivedValue):
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
   109
                #let it through
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
   110
                pass
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
   111
            else:            
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
   112
                try:
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
   113
                    validate = map[name].validate
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
   114
                    if not validate(value):
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   115
                        raise AttributeError("Illegal assignment of '%s' to '%s' in class %s" % (value, name, obj.__class__.__name__))
2547
9d13685212f2 datacharts synchronised
andy
parents: 2489
diff changeset
   116
                except KeyError:
3721
0c93dd8ff567 initial changes from 2to3-3.3
rptlab
parents: 3617
diff changeset
   117
                    raise AttributeError("Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__))
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   118
    obj.__dict__[name] = value
1549
df0706b21a94 Fixed to work with 2.2
rgbecker
parents: 948
diff changeset
   119
df0706b21a94 Fixed to work with 2.2
rgbecker
parents: 948
diff changeset
   120
def _privateAttrMap(obj,ret=0):
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   121
    '''clone obj._attrMap if required'''
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   122
    A = obj._attrMap
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   123
    oA = getattr(obj.__class__,'_attrMap',None)
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   124
    if ret:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   125
        if oA is A:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   126
            return A.clone(), oA
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   127
        else:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   128
            return A, None
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   129
    else:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   130
        if oA is A:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   131
            obj._attrMap = A.clone()
1549
df0706b21a94 Fixed to work with 2.2
rgbecker
parents: 948
diff changeset
   132
df0706b21a94 Fixed to work with 2.2
rgbecker
parents: 948
diff changeset
   133
def _findObjectAndAttr(src, P):
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   134
    '''Locate the object src.P for P a string, return parent and name of attribute
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   135
    '''
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   136
    P = string.split(P, '.')
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   137
    if len(P) == 0:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   138
        return None, None
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   139
    else:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   140
        for p in P[0:-1]:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   141
            src = getattr(src, p)
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   142
        return src, P[-1]
1549
df0706b21a94 Fixed to work with 2.2
rgbecker
parents: 948
diff changeset
   143
2025
22cff3d2ab78 Change name of hook func
rgbecker
parents: 1693
diff changeset
   144
def hook__setattr__(obj):
22cff3d2ab78 Change name of hook func
rgbecker
parents: 1693
diff changeset
   145
    if not hasattr(obj,'__attrproxy__'):
22cff3d2ab78 Change name of hook func
rgbecker
parents: 1693
diff changeset
   146
        C = obj.__class__
22cff3d2ab78 Change name of hook func
rgbecker
parents: 1693
diff changeset
   147
        import new
22cff3d2ab78 Change name of hook func
rgbecker
parents: 1693
diff changeset
   148
        obj.__class__=new.classobj(C.__name__,(C,)+C.__bases__,
22cff3d2ab78 Change name of hook func
rgbecker
parents: 1693
diff changeset
   149
            {'__attrproxy__':[],
22cff3d2ab78 Change name of hook func
rgbecker
parents: 1693
diff changeset
   150
            '__setattr__':lambda self,k,v,osa=getattr(obj,'__setattr__',None),hook=hook: hook(self,k,v,osa)})
1693
1a71e3509e39 Allow for multiple BASEs
rgbecker
parents: 1683
diff changeset
   151
1a71e3509e39 Allow for multiple BASEs
rgbecker
parents: 1683
diff changeset
   152
def addProxyAttribute(src,name,validate=None,desc=None,initial=None,dst=None):
1677
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   153
    '''
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   154
    Add a proxy attribute 'name' to src with targets dst
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   155
    '''
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   156
    #sanity
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   157
    assert hasattr(src,'_attrMap'), 'src object has no _attrMap'
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   158
    A, oA = _privateAttrMap(src,1)
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   159
    if type(dst) not in _SequenceTypes: dst = dst,
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   160
    D = []
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   161
    DV = []
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   162
    for d in dst:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   163
        if type(d) in _SequenceTypes:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   164
            d, e = d[0], d[1:]
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   165
        obj, attr = _findObjectAndAttr(src,d)
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   166
        if obj:
1450177dd19e Exterminated all tab characters and added a test to make sure
andy_robinson
parents: 1559
diff changeset
   167
            dA = getattr(obj,'_attrMap',None)