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