Package advene :: Package model :: Package core :: Module list
[hide private]
[frames] | no frames]

Source Code for Module advene.model.core.list

  1  """ 
  2  I define the class of lists. 
  3  """ 
  4  from weakref import ref 
  5   
  6  from advene.model.consts import _RAISE 
  7  from advene.model.core.element \ 
  8    import PackageElement, LIST 
  9  from advene.model.core.content import WithContentMixin 
 10  from advene.model.core.group import GroupMixin 
11 12 -class List(PackageElement, WithContentMixin, GroupMixin):
13 """ 14 I expose the protocol of a basic collection, to give access to the items 15 of a list. 16 17 I also try to efficiently cache the results I know. 18 """ 19 20 # Caching is performed as follow: 21 # `instantiate` retrieves the number of items, and builds self.__ids 22 # and self._cache, a list of id-refs and weakrefs respectively. 23 # Whenever an index is accessed, the item if retrieved from self._cache. 24 # If None, its id-ref is retrieved from self.__ids and the item is 25 # retrieved from the package. If the id-ref is None, the id-ref is 26 # retrieved from the backend. 27 28 ADVENE_TYPE = LIST 29 30 # attributes that do not prevent lists to be volatile 31 _cache = None 32 _ids = None 33 34 @classmethod
35 - def instantiate(cls, owner, id, *args):
36 r = super(List, cls).instantiate(owner, id, *args) 37 c = owner._backend.count_items(owner._id, id) 38 r._cache = [lambda: None,] * c 39 r._ids = [None,] * c 40 return r
41 42 @classmethod
43 - def create_new(cls, owner, id, items=()):
44 owner._backend.create_list(owner._id, id) 45 r = cls.instantiate(owner, id) 46 if items: 47 r.extend(items) 48 return r
49
50 - def _update_caches(self, old_idref, new_idref, element, relation):
51 """ 52 :see-also: `advene.model.core.element.PackageElement._update_caches` 53 """ 54 if relation.startswith(":item "): 55 index = int(relation[6:]) 56 self._ids[index] = new_idref 57 if element is not None: 58 self._cache[index] = ref(element) # just in case both were None 59 else: 60 try: 61 super(List, self) \ 62 ._update_caches(old_idref, new_idref, element, relation) 63 except AttributeError: 64 pass
65
66 - def __len__(self):
67 return len(self._cache)
68
69 - def __iter__(self):
70 """Iter over the items of this list. 71 72 If the list contains unreachable items, None is yielded instead. 73 74 See also `iter_item_ids`. 75 """ 76 for i,y in enumerate(self._cache): 77 y = y() # follow weak ref 78 if y is None: 79 y = self.get_item(i, None) 80 yield y
81
82 - def __getitem__(self, i):
83 """Return item with index i, or raise an exception if the item is 84 unreachable. 85 86 See also `get_item` and `get_item_id`. 87 """ 88 if isinstance(i, slice): return self._get_slice(i) 89 else: return self.get_item(i, _RAISE)
90
91 - def __setitem__(self, i, a):
92 if isinstance(i, slice): return self._set_slice(i, a) 93 assert hasattr(a, "ADVENE_TYPE"), "List %s does not specify an ADVENE_TYPE" % str(self) 94 o = self._owner 95 assert o._can_reference(a), "The list owner %s cannot reference %s" % (str(o), str(a)) 96 aid = a.make_id_in(o) 97 s = slice(i, i+1) 98 L = [a,] 99 self.emit("pre-modified-items", s, L) 100 self._ids[i] = aid 101 self._cache[i] = ref(a) 102 o._backend.update_item(o._id, self._id, aid, i) 103 self.emit("modified-items", s, L)
104
105 - def __delitem__(self, i):
106 if isinstance(i, slice): return self._del_slice(i) 107 s = slice(i, i+1) 108 self.emit("pre-modified-items", s, []) 109 del self._ids[i] # also guarantees that is is a valid index 110 del self._cache[i] 111 o = self._owner 112 o._backend.remove_item(o._id, self._id, i) 113 self.emit("modified-items", s, [])
114
115 - def _get_slice(self, s):
116 c = len(self._cache) 117 return [ self.get_item(i, _RAISE) for i in range(c)[s] ]
118
119 - def _set_slice(self, s, elements):
120 c = len(self._cache) 121 indices = range(c)[s] 122 same_length = (len(elements) == len(indices)) 123 if s.step is None and not same_length: 124 self._del_slice(s) 125 insertpoint = s.start or 0 126 for e in elements: 127 self.insert(insertpoint, e) 128 insertpoint += 1 129 else: 130 if not same_length: 131 raise ValueError("attempt to assign sequence of size %s to " 132 "extended slice of size %s" 133 % (len(elements), len(indices))) 134 for i,j in enumerate(indices): 135 self.__setitem__(j, elements[i])
136
137 - def _del_slice(self,s):
138 c = len(self._cache) 139 indices = range(c)[s] 140 indices.sort() 141 for offset, i in enumerate(indices): 142 del self[i-offset]
143
144 - def insert(self, i, a):
145 # this method accepts a strict id-ref instead of a real element 146 o = self._owner 147 assert o._can_reference(a), "The list owner %s cannot reference %s" % (str(o), str(a)) 148 149 if hasattr(a, "ADVENE_TYPE"): 150 aid = a.make_id_in(o) 151 else: 152 aid = unicode(a) 153 assert aid.find(":") > 0, "Expected *strict* id-ref" 154 a = None 155 c = len(self._cache) 156 if i > c : i = c 157 if i < -c: i = 0 158 if i < 0 : i += c 159 self._ids.insert(i,aid) 160 if a is not None: 161 a = ref(a) 162 else: 163 a = lambda: None 164 self._cache.insert(i,a) 165 o._backend.insert_item(o._id, self._id, aid, i, c)
166 # NB: it is important to pass to the backend the length c computed 167 # *before* inserting the item 168
169 - def append(self, a):
170 # this method accepts a strict id-ref instead of a real element 171 o = self._owner 172 assert o._can_reference(a), "The list owner %s cannot reference %s" % (str(o), str(a)) 173 if hasattr(a, "ADVENE_TYPE"): 174 aid = a.make_id_in(o) 175 else: 176 aid = unicode(a) 177 assert aid.find(":") > 0, "Expected *strict* id-ref" 178 a = None 179 c = len(self._cache) 180 s = slice(c,c) 181 L = [a,] 182 self.emit("pre-modified-items", s, L) 183 self._ids.append(aid) 184 if a is not None: 185 a = ref(a) 186 else: 187 a = lambda: None 188 self._cache.append(a) 189 o._backend.insert_item(o._id, self._id, aid, -1, c) 190 self.emit("modified-items", s, L)
191 # NB: it is important to pass to the backend the length c computed 192 # *before* appending the item 193
194 - def extend(self, elements):
195 for e in elements: 196 self.append(e)
197
198 - def iter_item_ids(self):
199 """Iter over the id-refs of the items of this list. 200 201 See also `__iter__`. 202 """ 203 for i,y in enumerate(self._ids): 204 if y is not None: 205 yield y 206 else: 207 yield self.get_item_id(i)
208
209 - def get_item(self, i, default=None):
210 """Return item with index i, or default if it can not be retrieved. 211 212 Note that if ``i`` is an invalid index, an IndexError will still be 213 raised. 214 215 See also `__getitem__` and `get_item_id`. 216 """ 217 # NB: internally, default can be passed _RAISE to force exceptions 218 assert isinstance(i, (int, long)), "The index must be an integer" 219 r = self._cache[i]() 220 if r is None: 221 o = self._owner 222 rid = self._ids[i] 223 if rid is None: 224 c = len(self._cache) 225 i = xrange(c)[i] # check index and convert negative 226 rid = self._ids[i] = \ 227 o._backend.get_item(o._id, self._id, i) 228 r = o.get_element(rid, default) 229 if r is not default: 230 self._cache[i] = ref(r) 231 return r
232
233 - def get_item_id(self, i):
234 """Return id-ref of the item with index i. 235 236 See also `__getitem__` and `get_item`. 237 """ 238 assert isinstance(i, (int, long)), "The index must be an integer" 239 r = self._ids[i] 240 if r is None: 241 o = self._owner 242 c = len(self._ids) 243 i = xrange(c)[i] # check index and convert negative 244 r = self._ids[i] = o._backend.get_item(o._id, self._id, i) 245 return r
246