47 |
47 |
48 def _ensure_str_key(key): |
48 def _ensure_str_key(key): |
49 return tuple(str(k) for k in key) |
49 return tuple(str(k) for k in key) |
50 |
50 |
51 |
51 |
|
52 def rtags_chain(rtag): |
|
53 """Return the rtags chain, starting from the given one, and going back through each parent rtag |
|
54 up to the root (i.e. which as no parent). |
|
55 """ |
|
56 while rtag is not None: |
|
57 yield rtag |
|
58 rtag = rtag._parent |
|
59 |
|
60 |
52 class RegistrableRtags(RegistrableInstance): |
61 class RegistrableRtags(RegistrableInstance): |
53 __registry__ = 'uicfg' |
62 __registry__ = 'uicfg' |
54 __select__ = yes() |
63 __select__ = yes() |
55 |
64 |
56 |
65 |
66 _allowed_values = None |
75 _allowed_values = None |
67 # _init expected to be a method (introduced in 3.17), while _initfunc a |
76 # _init expected to be a method (introduced in 3.17), while _initfunc a |
68 # function given as __init__ argument and kept for bw compat |
77 # function given as __init__ argument and kept for bw compat |
69 _init = _initfunc = None |
78 _init = _initfunc = None |
70 |
79 |
71 def __init__(self): |
80 def __init__(self, parent=None): |
72 self._tagdefs = {} |
81 self._tagdefs = {} |
|
82 self._parent = parent |
|
83 if parent is not None: |
|
84 assert parent.__class__ is self.__class__, \ |
|
85 'inconsistent class for parent rtag {0}'.format(parent) |
73 |
86 |
74 def __repr__(self): |
87 def __repr__(self): |
75 # find a way to have more infos but keep it readable |
88 # find a way to have more infos but keep it readable |
76 # (in error messages in case of an ambiguity for instance) |
89 # (in error messages in case of an ambiguity for instance) |
77 return '%s (%s): %s' % (id(self), self.__regid__, self.__class__) |
90 return '%s (%s): %s' % (id(self), self.__regid__, self.__class__) |
103 if ertype != '*' and ertype not in schema: |
116 if ertype != '*' and ertype not in schema: |
104 self.warning('removing rtag %s: %s, %s undefined in schema', |
117 self.warning('removing rtag %s: %s, %s undefined in schema', |
105 (stype, rtype, otype, tagged), value, ertype) |
118 (stype, rtype, otype, tagged), value, ertype) |
106 self.del_rtag(stype, rtype, otype, tagged) |
119 self.del_rtag(stype, rtype, otype, tagged) |
107 break |
120 break |
108 if self._init is not None: |
121 if self._parent is None and self._init is not None: |
109 self.apply(schema, self._init) |
122 self.apply(schema, self._init) |
110 |
123 |
111 def apply(self, schema, func): |
124 def apply(self, schema, func): |
112 for eschema in schema.entities(): |
125 for eschema in schema.entities(): |
113 if eschema.final: |
126 if eschema.final: |
120 sschema, oschema = tschema, eschema |
133 sschema, oschema = tschema, eschema |
121 func(sschema, rschema, oschema, role) |
134 func(sschema, rschema, oschema, role) |
122 |
135 |
123 # rtag declaration api #################################################### |
136 # rtag declaration api #################################################### |
124 |
137 |
|
138 def derive(self, module, select): |
|
139 """Return a derivated of this relation tag, associated to given module and selector. |
|
140 |
|
141 This derivated will hold a set of specific rules but delegate to its "parent" relation tags |
|
142 for unfound keys. |
|
143 |
|
144 >>> class_afs = uicfg.autoform_section.derive(__name__, is_instance('Class')) |
|
145 """ |
|
146 copied = self.__class__(self) |
|
147 copied.__module__ = module |
|
148 copied.__select__ = select |
|
149 return copied |
|
150 |
125 def tag_attribute(self, key, *args, **kwargs): |
151 def tag_attribute(self, key, *args, **kwargs): |
126 key = list(key) |
152 key = list(key) |
127 key.append('*') |
153 key.append('*') |
128 key.append('subject') |
154 key.append('subject') |
129 self.tag_relation(key, *args, **kwargs) |
155 self.tag_relation(key, *args, **kwargs) |
161 |
187 |
162 def del_rtag(self, *key): |
188 def del_rtag(self, *key): |
163 del self._tagdefs[key] |
189 del self._tagdefs[key] |
164 |
190 |
165 def get(self, *key): |
191 def get(self, *key): |
|
192 """Return value for the given key, by looking from the most specific key to the more |
|
193 generic (using '*' wildcards). For each key, look into this rtag and its parent rtags. |
|
194 """ |
166 for key in reversed(self._get_keys(*key)): |
195 for key in reversed(self._get_keys(*key)): |
167 try: |
196 for rtag in rtags_chain(self): |
168 return self._tagdefs[key] |
197 try: |
169 except KeyError: |
198 return rtag._tagdefs[key] |
170 continue |
199 except KeyError: |
|
200 continue |
171 return None |
201 return None |
172 |
202 |
173 def etype_get(self, etype, rtype, role, ttype='*'): |
203 def etype_get(self, etype, rtype, role, ttype='*'): |
174 if role == 'subject': |
204 if role == 'subject': |
175 return self.get(etype, rtype, ttype, role) |
205 return self.get(etype, rtype, ttype, role) |
190 self.tag_container_cls()) |
220 self.tag_container_cls()) |
191 rtags.add(tag) |
221 rtags.add(tag) |
192 return rtags |
222 return rtags |
193 |
223 |
194 def get(self, stype, rtype, otype, tagged): |
224 def get(self, stype, rtype, otype, tagged): |
|
225 """Return value for the given key, which is an union of the values found from the most |
|
226 specific key to the more generic (using '*' wildcards). For each key, look into this rtag |
|
227 and its parent rtags. |
|
228 """ |
195 rtags = self.tag_container_cls() |
229 rtags = self.tag_container_cls() |
196 for key in self._get_keys(stype, rtype, otype, tagged): |
230 for key in self._get_keys(stype, rtype, otype, tagged): |
197 try: |
231 for rtag in rtags_chain(self): |
198 rtags.update(self._tagdefs[key]) |
232 try: |
199 except KeyError: |
233 rtags.update(rtag._tagdefs[key]) |
200 continue |
234 break |
|
235 except KeyError: |
|
236 continue |
201 return rtags |
237 return rtags |
202 |
238 |
203 |
239 |
204 class RelationTagsDict(RelationTagsSet): |
240 class RelationTagsDict(RelationTagsSet): |
205 """This class associates a dictionary to each key.""" |
241 """This class associates a dictionary to each key.""" |