Trees | Indices | Help |
|
---|
|
1 from weakref import ref 2 3 from advene.model.consts import _RAISE, PARSER_META_PREFIX 4 from advene.model.exceptions import ModelError 5 from advene.model.tales import tales_property 6 from advene.util.alias import alias 7 from advene.util.sorted_dict import SortedDict10 """Metadata access mixin. 11 12 I factorize all metadata-related code for classes Package and 13 PackageElement. 14 15 I also provide an alias mechanism to make frequent metadata easily 16 accessible as python properties. 17 18 FIXME: expand description with usage example 19 """ 20 21 # NB: unlike other collections in the Advene model, metadata no not provide 22 # means to get id-ref *only* for unreachable elements. Since metadata 23 # already require a test (is the value a string value or an element value) 24 # mixing "pure" strings, id-refs and elements would seem too cumbersome... 25 26 __cache = None # SortedDict of all known metadata 27 # values are either string or (weakref, id) 28 __cache_is_complete = False # is self.__cache complete? 29316 317 def deller(obj): 318 return obj.del_meta(key) 319 320 if doc is None: 321 doc = "shortcut for metadata with key\n <%s>" % key 322 323 setattr(cls, alias, property(getter, setter, deller, doc)) 324 325 # @tales_path1_function 326 # def _tales_meta(self, path): 327 # nsd = self._get_ns_dict() 328 # return _PrefixDict(self, nsd[path]) 329 330 @tales_property31 """ 32 :see-also: `advene.model.core.element.PackageElement._update_caches` 33 """ 34 if relation.startswith(":meta ") and self.__cache is not None: 35 key = relation[6:] 36 if element is not None: 37 wref = ref(element) 38 else: 39 wref = lambda: None 40 self.__cache[key] = (wref, new_idref) 41 else: 42 try: 43 super(WithMetaMixin, self) \ 44 ._update_caches(old_idref, new_idref, element, relation) 45 except AttributeError: 46 pass4749 """Iter over all the metadata of this object. 50 51 Yields (key, value) pairs, where the value is either a string or an 52 element. If the element is unreachable, value is None. 53 54 See also `iter_meta_ids`. 55 """ 56 if hasattr(self, "ADVENE_TYPE"): 57 p = self._owner 58 eid = self._id 59 typ = self.ADVENE_TYPE 60 else: 61 p = self 62 eid = "" 63 typ = "" 64 65 if self.__cache_is_complete: 66 # then rely completely on cache 67 for k, v in self.__cache.iteritems(): 68 if v is KeyError: 69 continue 70 if isinstance(v, tuple): 71 tpl = v 72 v = tpl[0]() 73 if v is None: 74 v = p.get_element(tpl[1], None) 75 yield k, v 76 else: 77 # retrieve data from backend and cache them 78 cache = self.__cache 79 complete = True 80 if cache is None: 81 cache = self.__cache = SortedDict() 82 for k, v, v_is_id in p._backend.iter_meta(p._id, eid, typ): 83 if v_is_id: 84 # it is no use looking in cache: if the element is in it, 85 # then it will also be in the package's cache, and the 86 # retrieval from the package will be as efficient 87 e = p.get_element(v, None) 88 if e is not None: 89 cache[k] = (ref(e), v) 90 else: 91 complete = False 92 v = e 93 else: 94 cache[k] = v 95 yield k, v 96 self.__cache_is_complete = complete9799 """Iter over all the metadata of this object. 100 101 Yields (key, value) pairs, where the value is a string with a special 102 attribute ``is_id`` indicating if it represents the id-ref of an 103 element. 104 105 See also `iter_meta`. 106 """ 107 if hasattr(self, "ADVENE_TYPE"): 108 p = self._owner 109 eid = self._id 110 typ = self.ADVENE_TYPE 111 else: 112 p = self 113 eid = "" 114 typ = "" 115 116 if self.__cache_is_complete: 117 # then rely completely on cache 118 for k, v in self.__cache.iteritems(): 119 if v is KeyError: 120 continue 121 if isinstance(v, tuple): 122 v = metadata_value(v[1], True) 123 else: 124 v = metadata_value(v, False) 125 yield k, v 126 else: 127 # retrieve data from backend 128 cache = self.__cache 129 for k, v, v_is_id in p._backend.iter_meta(p._id, eid, typ): 130 yield k, metadata_value(v, v_is_id)131133 """Return the metadata (string or element) associated to the given key. 134 135 If no metadata is associated to the given key, a KeyError is raised. 136 If the given key references an unreachable element, a 137 `NoSuchElementError` or `UnreachableImportError` is raised. 138 139 All exceptions can be avoided by providing a ``default`` value, that 140 will be returned instead of raising an exception. 141 """ 142 return self._get_meta_id_or_ref(key, default, False)143145 """ 146 Return the metadata id (string or element) associated to the given key. 147 148 The returned value is a string with a special attribute ``is_id`` 149 indicating if it represents the id-ref of an element. 150 151 If no metadata is associated to the given key, a KeyError is raised, 152 unless ``default`` is provideded, in which case its value is returned 153 instead. 154 """ 155 # NB: this method actually implement both get_meta and get_meta_ids, 156 # with the flag _return_id to choose between the two. 157 158 if hasattr(self, "ADVENE_TYPE"): 159 p = self._owner 160 eid = self._id 161 typ = self.ADVENE_TYPE 162 else: 163 p = self 164 eid = "" 165 typ = "" 166 cache = self.__cache 167 if cache is None: 168 cache = self.__cache = SortedDict() 169 170 val = cache.get((key)) 171 if isinstance(val, tuple): 172 if _return_id: 173 val = metadata_value(val[1], True) 174 else: 175 wref, the_id = val 176 val = wref() 177 if val is None: 178 val = p.get_element(the_id, default) 179 if val is not default: 180 cache[key] = (ref(val), the_id) 181 elif isinstance(val, basestring): 182 if _return_id: 183 val = metadata_value(val, False) 184 elif val is None: # could also be KeyError 185 tpl = p._backend.get_meta(p._id, eid, typ, key) 186 if tpl is None: 187 val = cache[key] = KeyError 188 else: 189 if _return_id: 190 val = metadata_value(*tpl) 191 elif tpl[1]: 192 the_id = tpl[0] 193 val = p.get_element(the_id, default) 194 if val is not default: 195 cache[key] = (ref(val), tpl[0]) 196 else: 197 val = cache[key] = tpl[0] 198 199 if val is KeyError: # from cache or set above 200 if default is _RAISE: 201 raise KeyError(key) 202 else: 203 val = default 204 return val205 206 @alias(get_meta_id)208 # get_meta and get_meta_id have a common implementation. 209 # Normally, it should be located in a "private" method named 210 # _get_meta_id_or_ref. 211 # However, for efficiency reasons, that private method and 212 # get_meta_id have been merged into one. Both names are necessary 213 # because the "public" get_meta_id may be overridden while the 214 # "private" method should not. Hence that alias. 215 pass216218 """Set the metadata. 219 220 ``val`` can either be a PackageElement or a string. If an element, it 221 must be directly imported by the package of self, or a ModelError will 222 be raised. 223 224 Use `val_is_idref` only if you know what you are doing: it forces `val` 225 to be interpreted as an id-ref rather than a plain string; if the 226 id-ref has an import-prefix, only the existence of the import is 227 checked. This is mainly useful for parsers. 228 """ 229 if hasattr(self, "ADVENE_TYPE"): 230 p = self._owner 231 eid = self._id 232 typ = self.ADVENE_TYPE 233 else: 234 p = self 235 eid = "" 236 typ = "" 237 if hasattr(val, "ADVENE_TYPE"): 238 if not p._can_reference(val): 239 raise ModelError, "Element should be directy imported" 240 vstr = val.make_id_in(p) 241 vstr_is_id = True 242 val = (ref(val), vstr) 243 elif val_is_idref: 244 assert val.find(":") > 0, "Expected *strict* id-ref" 245 if not p._can_reference(val): 246 raise ModelError, "Element or import does not exist %s" % val 247 vstr = val 248 vstr_is_id = True 249 val = (None, val) 250 else: 251 vstr = unicode(val) 252 vstr_is_id = False 253 cache = self.__cache 254 if cache is None: 255 cache = self.__cache = SortedDict() 256 257 self.emit("pre-modified-meta::" + key, key, val) 258 cache[key] = val 259 p._backend.set_meta(p._id, eid, typ, key, vstr, vstr_is_id) 260 self.emit("modified-meta::" + key, key, val)261263 """Delete the metadata. 264 265 Note that if the given key is not in use, this will have no effect. 266 """ 267 if hasattr(self, "ADVENE_TYPE"): 268 p = self._owner 269 eid = self._id 270 typ = self.ADVENE_TYPE 271 else: 272 p = self 273 eid = "" 274 typ = "" 275 cache = self.__cache 276 self.emit("pre-modified-meta::" + key, key, None) 277 if cache is not None and key in cache: 278 del cache[key] 279 p._backend.set_meta(p._id, eid, typ, key, None, False) 280 self.emit("modified-meta::" + key, key, None)281 282 @property284 return _MetaDict(self)285 286 @classmethod288 """Attempts to create a python property in cls mapping to metadata key. 289 290 If alias is None, key is considered as a URI, and the last part of 291 that URI (after # or /) is used. 292 293 If default is not provided, an exception is raised whenever the 294 property is accessed when it is not set. Else, the default value will 295 be returned is that case. 296 297 If doc is not None or not provided, a simple docstring will be 298 generated. 299 300 Raises an AttributeError if cls already has a member with that name. 301 302 FIXME: should attach docstring to the property somehow 303 """ 304 if alias is None: 305 cut = max(key.rfind("#"), key.rfind("/")) 306 alias = key[cut+1:] 307 308 if hasattr(cls, alias): 309 raise AttributeError(alias) 310 311 def getter(obj): 312 return obj.get_meta(key, default)313 314 def setter(obj, val): 315 return obj.set_meta(key, val)332 d=dict( (qname, 333 dict( (k.replace(uri, ''), v) 334 for (k, v) in self.iter_meta() 335 if k.startswith(uri) ) ) 336 for (qname, uri) in self._get_ns_dict().iteritems() ) 337 return d338340 if hasattr(self, "ADVENE_TYPE"): 341 package = self.owner 342 else: 343 package = self 344 namespaces = {} 345 prefixes = package.get_meta(PARSER_META_PREFIX + "namespaces", "") 346 for line in prefixes.split("\n"): 347 if line: 348 prefix, uri = line.split(" ") 349 namespaces[prefix] = uri 350 return namespaces351354 """A dict-like object representing the metadata of an object. 355 356 Note that many methods have an equivalent with suffix ``_id`` or 357 ``_ids`` which use `get_meta_id` instead of `get_meta` and 358 `iter_meta_ids` instead of `iter_meta`, respectively. 359 """ 360 361 __slots__ = ["_owner",] 362 365490367 return self.get_meta(k, None) is not None368 371 374 377 380 383 387 390 393 396 399 402 405 408 411 414 417 420 423425 v = self._owner.get_meta(k, None) 426 if v is None: 427 if d is _RAISE: 428 raise KeyError, k 429 else: 430 v = d 431 else: 432 self._owner.del_meta(k) 433 return v434436 v = self._owner.get_meta_id(k, None) 437 if v is None: 438 if d is _RAISE: 439 raise KeyError, k 440 else: 441 v = d 442 else: 443 self._owner.del_meta(k) 444 return v445447 it = self._owner.iter_meta() 448 try: 449 k, v = it.next() 450 except StopIteration: 451 raise KeyError() 452 else: 453 self._owner.del_meta(k) 454 return v455457 it = self._owner.iter_meta_ids() 458 try: 459 k, v = it.next() 460 except StopIteration: 461 raise KeyError() 462 else: 463 self._owner.del_meta(k) 464 return v465467 assert isinstance(d, basestring) or hasattr(d, "ADVENE_TYPE"), "Default value must be a string or an Advene element" 468 v = self._owner.get_meta(k, None) 469 if v is None: 470 self._owner.set_meta(k, d) 471 v = d 472 return v473475 e_keys = getattr(e, "keys", None) 476 if callable(e_keys): 477 for k in e_keys(): 478 self._owner.set_meta(k, e[k]) 479 elif e is not None: 480 for k, v in e: 481 self._owner.set_meta(k, v) 482 for k, v in f.iteritems(): 483 self._owner.set_meta(k, v)484 487491 492 #class _PrefixDict(object): 493 # """ 494 # A dict-like object used as an intermediate object in TALES expression. 495 # """ 496 # __slots__ = ["_obj", "_prefix"] 497 # 498 # def __init__(self, obj, prefix): 499 # self._obj = obj 500 # self._prefix = prefix 501 # 502 # def __getitem__(self, path): 503 # return self._obj.get_meta(self._prefix+path) 504 # 505 # def __call__(self): 506 # # TODO use iter_meta(prefix=X) when implemented 507 # return ( (k[len(self._prefix):], v) 508 # for k,v in self._obj.iter_meta() 509 # if k.startswith(self._prefix)) 510 # 511 -class metadata_value(unicode):516
Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Wed Jul 8 16:00:06 2009 | http://epydoc.sourceforge.net |