1import rpy2.rlike.indexing as rli 2from typing import Any 3from typing import Iterable 4from typing import List 5from typing import Optional 6from typing import Tuple 7 8 9class OrdDict(dict): 10 """ Implements the Ordered Dict API defined in PEP 372. 11 When `odict` becomes part of collections, this class 12 should inherit from it rather than from `dict`. 13 14 This class differs a little from the Ordered Dict 15 proposed in PEP 372 by the fact that: 16 not all elements have to be named. None as a key value means 17 an absence of name for the element. 18 19 """ 20 21 __l: List[Tuple[Optional[str], Any]] 22 23 def __init__(self, c: Iterable[Tuple[Optional[str], Any]]=[]): 24 25 if isinstance(c, TaggedList) or isinstance(c, OrdDict): 26 c = c.items() 27 elif isinstance(c, dict): 28 # FIXME: allow instance from OrdDict ? 29 raise TypeError('A regular dictionnary does not ' + 30 'conserve the order of its keys.') 31 32 super(OrdDict, self).__init__() 33 self.__l = [] 34 35 for k, v in c: 36 self[k] = v 37 38 def __copy__(self): 39 cp = OrdDict(c=tuple(self.items())) 40 return cp 41 42 def __cmp__(self, o): 43 raise(NotImplementedError("Not yet implemented.")) 44 45 def __eq__(self, o): 46 raise(NotImplementedError("Not yet implemented.")) 47 48 def __getitem__(self, key: str): 49 if key is None: 50 raise ValueError("Unnamed items cannot be retrieved by key.") 51 i = super(OrdDict, self).__getitem__(key) 52 return self.__l[i][1] 53 54 def __iter__(self): 55 seq = self.__l 56 for e in seq: 57 k = e[0] 58 if k is None: 59 continue 60 else: 61 yield k 62 63 def __len__(self): 64 return len(self.__l) 65 66 def __ne__(self, o): 67 raise(NotImplementedError('Not yet implemented.')) 68 69 def __repr__(self) -> str: 70 s = ['o{', ] 71 for k, v in self.items(): 72 s.append("'%s': %s, " % (str(k), str(v))) 73 s.append('}') 74 return ''.join(s) 75 76 def __reversed__(self): 77 raise(NotImplementedError("Not yet implemented.")) 78 79 def __setitem__(self, key: Optional[str], value: Any): 80 """ Replace the element if the key is known, 81 and conserve its rank in the list, or append 82 it if unknown. """ 83 84 if key is None: 85 self.__l.append((key, value)) 86 return 87 88 if key in self: 89 i = self.index(key) 90 self.__l[i] = (key, value) 91 else: 92 self.__l.append((key, value)) 93 super(OrdDict, self).__setitem__(key, len(self.__l)-1) 94 95 def byindex(self, i: int) -> Any: 96 """ Fetch a value by index (rank), rather than by key.""" 97 return self.__l[i] 98 99 def index(self, k: str) -> int: 100 """ Return the index (rank) for the key 'k' """ 101 return super(OrdDict, self).__getitem__(k) 102 103 def get(self, k: str, d: Any = None): 104 """ OD.get(k[,d]) -> OD[k] if k in OD, else d. d defaults to None """ 105 try: 106 res = self[k] 107 except KeyError: 108 res = d 109 return res 110 111 def items(self): 112 """ OD.items() -> an iterator over the (key, value) items of D """ 113 return iter(self.__l) 114 115 def keys(self): 116 """ """ 117 return tuple([x[0] for x in self.__l]) 118 119 def reverse(self): 120 """ Reverse the order of the elements in-place (no copy).""" 121 seq = self.__l 122 n = len(self.__l) 123 for i in range(n//2): 124 tmp = seq[i] 125 seq[i] = seq[n-i-1] 126 kv = seq[i] 127 if kv is not None: 128 super(OrdDict, self).__setitem__(kv[0], i) 129 seq[n-i-1] = tmp 130 kv = tmp 131 if kv is not None: 132 super(OrdDict, self).__setitem__(kv[0], n-i-1) 133 134 def sort(self, cmp=None, key=None, reverse=False): 135 raise NotImplementedError("Not yet implemented.") 136 137 138class TaggedList(list): 139 """ A list for which each item has a 'tag'. 140 141 :param l: list 142 :param tag: optional sequence of tags 143 """ 144 145 __tags: List[Optional[str]] 146 147 def __init__(self, seq, tags=None): 148 super(TaggedList, self).__init__(seq) 149 if tags is None: 150 tags = [None, ] * len(seq) 151 if len(tags) != len(seq): 152 raise ValueError("There must be as many tags as seq") 153 self.__tags = list(tags) 154 155 def __add__(self, tl): 156 try: 157 tags = tl.tags 158 except AttributeError: 159 raise ValueError('Can only concatenate TaggedLists.') 160 res = TaggedList(list(self) + list(tl), 161 tags=self.tags + tags) 162 return res 163 164 def __delitem__(self, y): 165 super(TaggedList, self).__delitem__(y) 166 self.__tags.__delitem__(y) 167 168 def __delslice__(self, i, j): 169 super(TaggedList, self).__delslice__(i, j) 170 self.__tags.__delslice__(i, j) 171 172 def __iadd__(self, y): 173 super(TaggedList, self).__iadd__(y) 174 if isinstance(y, TaggedList): 175 self.__tags.__iadd__(y.tags) 176 else: 177 self.__tags.__iadd__([None, ] * len(y)) 178 return self 179 180 def __imul__(self, y): 181 restags = self.__tags.__imul__(y) 182 resitems = super(TaggedList, self).__imul__(y) 183 return self 184 185 @staticmethod 186 def from_items(tagval): 187 res = TaggedList([]) 188 for k, v in tagval.items(): 189 res.append(v, tag=k) 190 return res 191 192 def __setslice__(self, i, j, y): 193 super(TaggedList, self).__setslice__(i, j, y) 194 # TODO: handle TaggedList ? 195 # self.__tags.__setslice__(i, j, [None, ]) 196 197 def append(self, obj, tag=None): 198 """ Append an object to the list 199 :param obj: object 200 :param tag: object 201 """ 202 super(TaggedList, self).append(obj) 203 self.__tags.append(tag) 204 205 def extend(self, iterable): 206 """ Extend the list with an iterable object. 207 208 :param iterable: iterable object 209 """ 210 211 if isinstance(iterable, TaggedList): 212 itertags = iterable.itertags() 213 else: 214 itertags = [None, ] * len(iterable) 215 216 for tag, item in zip(itertags, iterable): 217 self.append(item, tag=tag) 218 219 def insert(self, index, obj, tag=None): 220 """ 221 Insert an object in the list 222 223 :param index: integer 224 :param obj: object 225 :param tag: object 226 """ 227 super(TaggedList, self).insert(index, obj) 228 self.__tags.insert(index, tag) 229 230 def iterontag(self, tag): 231 """ 232 iterate on items marked with one given tag. 233 234 :param tag: object 235 """ 236 237 i = 0 238 for onetag in self.__tags: 239 if tag == onetag: 240 yield self[i] 241 i += 1 242 243 def items(self): 244 """ OD.items() -> an iterator over the (key, value) items of D """ 245 for tag, item in zip(self.__tags, self): 246 yield (tag, item) 247 248 def itertags(self): 249 """ 250 iterate on tags. 251 252 :rtype: iterator 253 """ 254 for tag in self.__tags: 255 yield tag 256 257 def pop(self, index=None): 258 """ 259 Pop the item at a given index out of the list 260 261 :param index: integer 262 263 """ 264 if index is None: 265 index = len(self) - 1 266 267 res = super(TaggedList, self).pop(index) 268 self.__tags.pop(index) 269 return res 270 271 def remove(self, value): 272 """ 273 Remove a given value from the list. 274 275 :param value: object 276 277 """ 278 found = False 279 for i in range(len(self)): 280 if self[i] == value: 281 found = True 282 break 283 if found: 284 self.pop(i) 285 286 def reverse(self): 287 """ Reverse the order of the elements in the list. """ 288 super(TaggedList, self).reverse() 289 self.__tags.reverse() 290 291 def sort(self, reverse=False): 292 """ 293 Sort in place 294 """ 295 o = rli.order(self, reverse=reverse) 296 super(TaggedList, self).sort(reverse=reverse) 297 self.__tags = [self.__tags[i] for i in o] 298 299 def __get_tags(self): 300 return tuple(self.__tags) 301 302 def __set_tags(self, tags): 303 if len(tags) == len(self.__tags): 304 self.__tags = tuple(tags) 305 else: 306 raise ValueError('The new list of tags should have the ' 307 'same length as the old one.') 308 309 tags = property(__get_tags, __set_tags) 310 311 def settag(self, i, t): 312 """ 313 Set tag 't' for item 'i'. 314 315 :param i: integer (index) 316 317 :param t: object (tag) 318 """ 319 self.__tags[i] = t 320