Package csb :: Package core
[frames] | no frames]

Source Code for Package csb.core

  1  """ 
  2  Generic containers, data structures and language extensions. 
  3   
  4  This module has several functions: 
  5   
  6      1. provides a set of reusable, probably encapsulated collection containers 
  7         and supporting infrastructure: L{BaseDictionaryContainer}, 
  8         L{BaseCollectionContainer}; also L{AbstractIndexer} 
  9       
 10      2. provides some missing generic data structures or data types: 
 11         L{OrderedDict}, L{Stack}; also the heavily used, type-safe L{enum} class 
 12         and some generic pattern implementations like L{singleton} or L{Proxy} 
 13       
 14      3. serves as a compatibility layer, making it possible to develop CSB as 
 15         platform- and Python version-independent: string, L{iterable}, L{metaclass} 
 16   
 17  In order to ensure cross-interpreter compatibility, checking for string instances 
 18  in CSB must always be implemented like this: 
 19   
 20      >>> isinstance("s", string) 
 21   
 22  because "basestring" is not available in Python 3. Also, metaclass definitions 
 23  other than abstract classes must be implemented as follows: 
 24   
 25      >>> MyClassBase = metaclass(MetaClass, base=BaseClass) 
 26      >>> class MyClass(MyClassBase): 
 27              pass 
 28               
 29  See also the notes about compatibility in L{csb.io}.              
 30  """ 
 31   
 32  import re 
 33  import sys 
 34  import time 
 35  import threading 
 36   
 37  from abc import ABCMeta, abstractproperty, abstractmethod 
 38   
 39  try: 
 40      string = basestring 
 41  except NameError: 
 42      string = str 
43 44 45 -class InterruptibleThread(threading.Thread):
46
47 - def __init__(self, target, name=None, args=[], kwargs={}):
48 49 super(InterruptibleThread, self).__init__(target=target, name=name, args=args, kwargs=kwargs) 50 self.setDaemon(True) 51 self.__result = None 52 self.__target = target 53 self.__name = name 54 self.__args = args 55 self.__kwargs = kwargs
56 57 @property
58 - def result(self):
59 return self.__result
60
61 - def run(self):
62 try: 63 self.__result = self.__target(*self.__args, **self.__kwargs) 64 except Exception as ex: 65 sys.stderr.write(ex) 66 self.__result = None
67
68 -def singleton(klass):
69 """ 70 Singleton class decorator. 71 """ 72 instances = {} 73 74 def get(): 75 if klass not in instances: 76 instances[klass] = klass() 77 return instances[klass]
78 79 return get 80
81 -class Proxy(object):
82 """ 83 Base class implementing the proxy pattern. Subclasses that define 84 a customized constructor must call super().__init__(subject) for proper 85 initialization. 86 87 @param subject: an arbitrary object, hidden behind the proxy 88 """ 89
90 - def __init__(self, subject):
91 self._subject = subject
92
93 - def __getattr__(self, name):
94 try: 95 return object.__getattribute__(self, name) 96 except AttributeError: 97 subject = object.__getattribute__(self, '_subject') 98 return getattr(subject, name)
99
100 -class REMatchProxy(object):
101
102 - def __init__(self, match):
103 104 self._start = [match.start()] 105 self._end = [match.end()] 106 self._groups = match.groups() 107 108 for i, dummy in enumerate(self._groups, start=1): 109 self._start.append(match.start(i)) 110 self._end.append(match.end(i))
111
112 - def start(self, group=0):
113 try: 114 if not group >= 0: 115 raise IndexError 116 return self._start[group] 117 except IndexError: 118 raise IndexError('no such group')
119
120 - def end(self, group=0):
121 try: 122 if not group >= 0: 123 raise IndexError 124 return self._end[group] 125 except IndexError: 126 raise IndexError('no such group')
127
128 - def groups(self):
129 return self._groups
130
131 -class Stack(list):
132
133 - def push(self, item):
134 """ 135 Push an item onto the top of the stack 136 """ 137 self.append(item)
138
139 - def peek(self):
140 """ 141 Return the object at the top of the stack without removing it 142 """ 143 if self.empty(): 144 raise IndexError('peek in empty list') 145 return self[-1]
146
147 - def empty(self):
148 """ 149 Return True if the stack is empty 150 """ 151 return len(self) == 0
152
153 -def iterable(obj):
154 """ 155 Return True if C{obj} is a collection or iterator, but not string. 156 This is guaranteed to work in both python 2 and 3. 157 158 @rtype: bool 159 """ 160 if hasattr(obj, '__iter__'): 161 if not isinstance(obj, string): 162 return True 163 164 return False
165
166 -def metaclass(meta, base=object):
167 """ 168 Return a new class with parent class C{base} and metaclass C{meta}. 169 This works in both python 2 and 3. 170 """ 171 return meta("NewBase", (base,), {})
172
173 -def deepcopy(obj, recursion=100000):
174 """ 175 Perform a deep copy of obj using cPickle. Faster than copy.deepcopy() 176 for large objects. 177 178 @param obj: the object to copy 179 @return: a deep copy of obj 180 @param recursion: maximum recursion limit 181 @type recursion: int 182 """ 183 from csb.io import Pickle 184 185 current = sys.getrecursionlimit() 186 sys.setrecursionlimit(recursion) 187 188 tmp = Pickle.dumps(obj, Pickle.HIGHEST_PROTOCOL) 189 copy = Pickle.loads(tmp) 190 191 sys.setrecursionlimit(current) 192 return copy
193
194 -def _deserialize_enum(enum, name):
195 return getattr(enum, name)
196
197 -class EnumValueError(ValueError):
198 pass
199
200 -class EnumMemberError(AttributeError):
201 pass
202
203 -class EnumItem(object):
204
205 - def __init__(self, name, value, enum):
206 self.__name = name 207 self.__value = value 208 self.__container = enum
209
210 - def __reduce__(self):
211 return (_deserialize_enum, (self.enum, self.name))
212 - def __deepcopy__(self, memo):
213 return self
214 - def __copy__(self):
215 return self
216 - def __repr__(self):
217 return '{0}'.format(self.__name, self.__value)
218 - def __str__(self):
219 return str(self.__value)
220 - def __int__(self):
221 return int(self.__value)
222 - def __float__(self):
223 return float(self.__value)
224 - def __hash__(self):
225 return hash(self.__value)
226 - def __lt__(self, other):
227 if isinstance(other, EnumItem): 228 return self.__value < other.value 229 else: 230 return self.__value < other
231 - def __eq__(self, other):
232 if isinstance(other, EnumItem): 233 return self.__value == other.value 234 else: 235 return self.__value == other
236 - def __ne__(self, other):
237 return not (self == other)
238 239 @property
240 - def name(self):
241 return self.__name
242 243 @property
244 - def value(self):
245 return self.__value
246 247 @property
248 - def enum(self):
249 return self.__container
250
251 - def _attach(self, enum):
252 assert getattr(enum, self.__name) is self 253 self.__container = enum
254
255 -class EnumMeta(type):
256 """ 257 Metaclass for enum types. 258 """ 259
260 - def __new__(cls, name, bases, items):
261 262 enumdict = {} 263 264 enumdict['_name'] = name 265 enumdict['_names'] = {} 266 enumdict['_namesci'] = {} 267 enumdict['_values'] = {} 268 enumdict['_valuesci'] = {} 269 270 for attribute in items: 271 value = items[attribute] 272 273 if attribute.startswith('_'): 274 enumdict[attribute] = value 275 else: 276 if value in enumdict['_values']: 277 raise EnumValueError('Duplicate value {0} in enum {1}.'.format(value, name)) 278 279 enumdict['_names'][attribute] = attribute 280 enumdict['_namesci'][attribute.lower()] = attribute 281 enumdict['_values'][value] = attribute 282 if isinstance(value, string): 283 enumdict['_valuesci'][value.lower()] = attribute 284 285 enumdict[attribute] = EnumItem(attribute, value, None) 286 287 enum = super(EnumMeta, cls).__new__(cls, name, bases, enumdict) 288 289 for attribute in items: 290 if not attribute.startswith('_'): 291 value = items[attribute] 292 enum.__dict__[attribute]._attach(enum) 293 294 return enum
295
296 - def __setattr__(self, attribute, value):
297 raise AttributeError("enum types are immutable")
298
299 - def __repr__(self):
300 items = list(self._names)[:2] 301 preview = [ '{0}={1}'.format(i, getattr(self, i)) for i in items ] 302 if len(preview) < len(self._names): 303 preview.append('...') 304 return '<{0}: {1}>'.format(self._name, ', '.join(preview))
305
306 - def __str__(self):
307 return repr(self)
308 309 EnumBase = metaclass(EnumMeta)
310 311 -class enum(EnumBase):
312 """ 313 Base class for all enumeration types. Supports both string and integer 314 enumeration values. Examples: 315 316 >>> class MolTypes(enum): DNA, RNA = range(2) 317 <MolTypes: RNA=1, DNA=0> 318 >>> class MolTypes(enum): DNA=1; RNA=2 319 <MolTypes: RNA=2, DNA=1> 320 >>> MolTypes.DNA 321 1 322 >>> MolTypes.DNA == 1 323 True 324 >>> int(MolTypes.DNA) 325 1 326 >>> repr(MolTypes.DNA) 327 'DNA' 328 >>> type(MolTypes.DNA) 329 L{EnumItem} instance 330 331 @note: The recommended way to create an enum is to define a public 332 subclass of L{enum} in the global namespace of your module. Nesting 333 the enum in another class for example will break pickling. 334 """
335 - def __init__(self):
336 raise TypeError("Can't instantiate static enum type {0}".format(self.__class__))
337
338 -class Enum(object):
339 """ 340 A collection of efficient static methods for working with L{enum} 341 classes. 342 """ 343 344 @staticmethod
345 - def create(classname, **members):
346 """ 347 Dynamically create a new enum from a list of key:value pairs. 348 Note that each key must be a valid python identifier, and the 349 values must be unique. 350 351 @param classname: class name for the new enum 352 @type classname: str 353 354 @note: The recommended way to create an enum is to define a public 355 subclass of L{enum} in the global namespace of your module. 356 You should avoid creating enums dynamically if static 357 construction is possible, because dynamically created enums 358 cannot be pickled. 359 """ 360 361 return type(classname, (enum,), members)
362 363 @staticmethod
364 - def members(enumclass):
365 """ 366 Return all member items of the C{enumclass}. 367 368 @param enumclass: the enumeration class to traverse 369 @type enumclass: type 370 371 @return: a set of all enumclass members 372 @rtype: frozenset 373 """ 374 return frozenset([enumclass.__dict__[i] for i in enumclass._names])
375 376 @staticmethod
377 - def names(enumclass):
378 """ 379 Return the names of all items in the C{enumclass}. 380 381 @param enumclass: the enumeration class to traverse 382 @type enumclass: type 383 384 @return: a set of all enumclass member names 385 @rtype: frozenset 386 """ 387 return frozenset(enumclass._names)
388 389 @staticmethod
390 - def values(enumclass):
391 """ 392 Return all values of the C{enumclass}. 393 394 @param enumclass: the enumeration class to traverse 395 @type enumclass: type 396 397 @return: a set of all enum values 398 @rtype: frozenset 399 """ 400 return frozenset(enumclass._values)
401 402 @staticmethod
403 - def parse(enumclass, value, ignore_case=True):
404 """ 405 Parse C{value} as an C{enumclass} member value. 406 407 @param enumclass: target L{enum} subclass 408 @type enumclass: type 409 @type value: str, int, float 410 @param ignore_case: if set to True, triggers case insensitive parsing 411 @type ignore_case: bool 412 413 @return: a member of enumclass, having such value 414 @rtype: L{EnumItem} 415 416 @raise EnumValueError: when such value is not found in enumclass 417 """ 418 419 if isinstance(value, string) and ignore_case: 420 values = enumclass._valuesci 421 value = value.lower() 422 else: 423 values = enumclass._values 424 425 if value in values: 426 return enumclass.__dict__[ values[value] ] 427 else: 428 raise EnumValueError('No such value {0} in {1}'.format(value, enumclass))
429 430 @staticmethod
431 - def parsename(enumclass, name, ignore_case=True):
432 """ 433 Parse C{name} as a member of C{enumclass}. 434 435 @param enumclass: target L{enum} subclass 436 @type enumclass: type 437 @param name: enumclass member name to parse 438 @type name: str 439 @param ignore_case: if set to True, triggers case insensitive parsing 440 @type ignore_case: bool 441 442 @return: a member of enumclass, having such name 443 @rtype: L{EnumItem} 444 445 @raise EnumValueError: when such name is not found in enumclass's members 446 """ 447 448 if isinstance(name, string) and ignore_case: 449 names = enumclass._namesci 450 name = name.lower() 451 else: 452 names = enumclass._names 453 454 if name in names: 455 return enumclass.__dict__[ names[name] ] 456 else: 457 raise EnumMemberError('No such item {0} in {1}'.format(name, enumclass))
458 459 @staticmethod
460 - def tostring(item):
461 """ 462 Return a string representation of the enum item. 463 464 @param item: an enum item to be converted to a string 465 @type item: L{EnumItem} 466 467 @return: the value of the enum member 468 @rtype: str 469 """ 470 return item.name
471 472 @staticmethod
473 - def ismember(item, enumclass):
474 """ 475 Return True if item is a member of enumclass. 476 477 @param enumclass: target enumeration type 478 @type enumclass: type 479 @param item: the enum item to be tested 480 @type item: L{EnumItem} 481 @rtype: bool 482 """ 483 if not issubclass(enumclass, enum): 484 raise TypeError(enumclass) 485 return item.enum is enumclass
486
487 -class ItemNotFoundError(KeyError):
488 pass
489
490 -class InvalidKeyError(KeyError):
491 pass
492
493 -class DuplicateKeyError(InvalidKeyError):
494 pass
495
496 -class AbstractIndexer(object):
497 498 @abstractmethod
499 - def __getitem__(self, i):
500 pass
501 502 @abstractmethod
503 - def _getinternal(self, i):
504 """ 505 Implementing classes are expected to provide direct access to the 506 requested element in the internal data structure via this method. 507 """ 508 pass
509
510 -class BaseDictionaryContainer(AbstractIndexer):
511 """ 512 Base class which defines the behavior of a read only key-value collection 513 container. 514 515 @note: Methods for editing an existing dictionary are also defined in the 516 base class, but left internal on purpose. Subclasses which are 517 supposed to be write-enabled containers must provide their own 518 public methods for editing which might do some custom work and then 519 finally call any of the internal methods in the base class to do the 520 real data editing. 521 522 @param items: an initialization dictionary 523 @param restrict: a list of keys allowed for this dictionary 524 """ 525
526 - def __init__(self, items=None, restrict=None):
527 528 self._keys = None 529 self._items = OrderedDict({}) 530 531 if restrict: 532 self._keys = frozenset(restrict) 533 if items is not None: 534 self._set_items(items)
535
536 - def __getitem__(self, key):
537 try: 538 return self._items[key] 539 except KeyError: 540 raise self._exception(key)
541
542 - def _getinternal(self, key):
543 return self._items[key]
544
545 - def __contains__(self, item):
546 return item in self._items
547
548 - def __len__(self):
549 return len(self._items)
550
551 - def __nonzero__(self):
552 return self.__bool__()
553
554 - def __bool__(self):
555 return len(self) > 0
556 557 @property
558 - def _exception(self):
559 return ItemNotFoundError
560 561 @property
562 - def length(self):
563 return len(self)
564
565 - def keys(self):
566 return self._items.keys()
567
568 - def __iter__(self):
569 return iter(self._items)
570
571 - def __repr__(self):
572 return '<{0.__class__.__name__}: {0.length} items>'.format(self)
573
574 - def _set_items(self, new_items):
575 new_items = OrderedDict(new_items) 576 577 if self._keys and not self._keys.issuperset(new_items): 578 raise InvalidKeyError("One or more of the keys provided are not allowed for this collection.") 579 580 self._items = new_items
581
582 - def _update_items(self, new_items={ }, **named_items):
583 new_items = OrderedDict(new_items) 584 585 if self._keys: 586 if not set(self).issuperset(new_items) or not set(self).issuperset(named_items): 587 raise ItemNotFoundError("One or more of the keys provided were not found in this collection.") 588 589 self._items.update(new_items) 590 self._items.update(named_items)
591
592 - def _append_item(self, key, item):
593 594 if self._keys and key not in self._keys: 595 raise InvalidKeyError("Key {0} is not allowed for this collection.".format(key)) 596 if key in self: 597 raise DuplicateKeyError("Key {0} already exists in the collection.".format(key)) 598 self._items[key] = item
599
600 - def _remove_item(self, key):
601 602 if key not in self._items: 603 raise self._exception(key) 604 del self._items[key]
605
606 -class ReadOnlyDictionaryContainer(BaseDictionaryContainer):
607 """ 608 This is a write-once container, which is filled with items only at object 609 construction. 610 """ 611 pass
612
613 -class DictionaryContainer(BaseDictionaryContainer):
614 """ 615 Write-enabled Dictionary Container. New items can be added using a public 616 C{append} method. Subclasses may also override the internal C{_update} 617 and C{_set} to expose them to the clients. 618 """
619 - def __init__(self, items=None, restrict=None):
620 621 super(DictionaryContainer, self).__init__(items, restrict)
622
623 - def append(self, key, item):
624 """ 625 Append a new key-value to the collection. 626 627 @param key: key 628 @param item: value 629 630 @raise InvalidKeyError: when the key is not allowed for this container 631 @raise DuplicateKeyError: when such a key already exists 632 """ 633 self._append_item(key, item)
634
635 - def _set(self, new_items):
636 """ 637 Set the collection to the dictionary provided in the new_items parameter. 638 639 @param new_items: the new value of the dictionary container 640 @type new_items: dict 641 """ 642 self._set_items(new_items)
643
644 - def _update(self, new_items={ }, **named_items):
645 """ 646 Update the collection with the dictionary provided in the C{new_items} parameter. 647 Update also specific items with the values provided as keyword arguments. 648 649 @param new_items: a dictionary that provides new values for certain keys 650 @type new_items: dict 651 """ 652 self._update_items(new_items, **named_items)
653
654 - def _remove(self, item):
655 """ 656 Delete C{item}. 657 658 @param item: key to remove 659 """ 660 self._remove_item(item)
661
662 -class CollectionIndexError(IndexError):
663 pass
664
665 -class BaseCollectionContainer(AbstractIndexer):
666 """ 667 Base class which defines the behavior of a read-only collection container. 668 669 @note: Methods for editing an existing collection are also defined in the 670 base class, but left internal on purpose. Subclasses which are 671 supposed to be write-enabled containers must provide their own 672 public methods for editing which might do some custom work and then 673 finally call any of the internal methods in the base class to do the 674 real data editing. 675 676 @param items: initialization list 677 @type items: list 678 @param type: if defined, makes the container type-safe 679 @type type: type 680 @param start_index: the index of the zero element 681 @type start_index: int 682 """ 683
684 - def __init__(self, items=None, type=None, start_index=0):
685 686 self._items = [ ] 687 self._type = type 688 689 if not (isinstance(start_index, int) or start_index >= 0): 690 raise ValueError('start_index must be a positive integer.') 691 692 self._start = start_index 693 694 if items is not None: 695 self._update_items(items)
696
697 - def __fix(self, i):
698 if i is not None and i >= 0: 699 if i < self._start: 700 return None 701 return i - self._start 702 else: 703 return i
704
705 - def __getitem__(self, i):
706 try: 707 if isinstance(i, slice): 708 sl = slice(self.__fix(i.start), self.__fix(i.stop), i.step) 709 return self._items[sl] 710 else: 711 if 0 <= i < self._start: 712 raise IndexError 713 return self._items[self.__fix(i)] 714 715 except IndexError: 716 if len(self) > 0: 717 raise self._exception('Position {0} is out of range [{1}, {2}]'.format( 718 i, self.start_index, self.last_index)) 719 else: 720 raise self._exception('This collection is empty.')
721
722 - def _getinternal(self, i):
723 return self._items[i]
724
725 - def __contains__(self, item):
726 return item in self._items
727
728 - def __len__(self):
729 return len(self._items)
730
731 - def __nonzero__(self):
732 return self.__bool__()
733
734 - def __bool__(self):
735 return len(self) > 0
736 737 @property
738 - def _exception(self):
740 741 @property
742 - def length(self):
743 return len(self)
744 745 @property
746 - def start_index(self):
747 return self._start
748 749 @property
750 - def last_index(self):
751 length = len(self._items) 752 if length > 0: 753 return length + self._start - 1 754 else: 755 return None
756
757 - def __iter__(self):
758 return iter(self._items)
759
760 - def __repr__(self):
761 return '<{0.__class__.__name__}: {0.length} items>'.format(self)
762
763 - def _append_item(self, item):
764 765 if self._type: 766 if not isinstance(item, self._type): 767 raise TypeError("Item {0} is not of the required {1} type.".format( 768 item, self._type.__name__)) 769 self._items.append(item) 770 771 return len(self) + self._start - 1
772
773 - def _update_items(self, new_items):
774 775 if self._type: 776 for a in new_items: 777 if not isinstance(a, self._type): 778 raise TypeError("Item {0} is not of the required {1} type.".format( 779 a, self._type.__name__)) 780 self._items = list(new_items)
781
782 - def _sort(self):
783 self._items.sort()
784
785 -class ReadOnlyCollectionContainer(BaseCollectionContainer):
786 """ 787 This is a write-once container, which is filled with items only at object 788 construction. 789 """ 790 pass
791
792 -class CollectionContainer(BaseCollectionContainer):
793 """ 794 Write-enabled Collection Container. New items can be added using a public 795 C{append} method. Subclasses may also override the internal C{_update} 796 to expose it to the clients. 797 """ 798
799 - def __init__(self, items=None, type=None, start_index=0):
802
803 - def append(self, item):
804 """ 805 Append a new item to the collection. 806 807 @param item: the new item to append 808 809 @return: the index of the new element 810 @rtype: int 811 812 @raise TypeError: when the container is type-safe and item has an 813 incorrect type 814 """ 815 return self._append_item(item)
816
817 - def _update(self, new_items):
818 """ 819 Set the collection to the list provided in the new_items parameter. 820 821 @param new_items: a list providing the new items for the container 822 @type new_items: list 823 """ 824 self._update_items(new_items)
825
826 -class AbstractContainer(object):
827 """ 828 Defines the behavior of a high-level object, which can hold an array of 829 elements. Implementing classes automatically provide iterable and index/key 830 based access to those objects in a read-only encapsulated manner. 831 832 This is an abstract class with an abstract property C{_children}. Subclasses 833 must override this property. The overridden implementation is usually 834 extremely simple - you just need to return a reference to an iterable and 835 subscriptable object, containing the elements. 836 """ 837 838 __metaclass__ = ABCMeta 839
840 - def __getitem__(self, i):
841 return self._children[i]
842
843 - def __len__(self):
844 return len(self._children)
845
846 - def __nonzero__(self):
847 return True
848 849 __bool__ = __nonzero__ 850
851 - def __iter__(self):
852 for i in self._children: 853 yield i
854 855 @abstractproperty
856 - def _children(self):
857 pass
858
859 -class Container(AbstractContainer):
860 """ 861 Generic implementation of L{AbstractContainer}. Provides an easy way to 862 encapsulate class properties that behave like read-only collections or 863 dictionaries. This container is super lightweight and completely dynamic: 864 it serves as a proxy to an internal list or dict and stores no data in its 865 own instance. Owning classes are therefore free to modify those internal 866 data structures, which provides advantages over using 867 L{ReadOnlyCollectionContainer}s. 868 """ 869
870 - def __init__(self, data):
871 self._data = data
872 873 @property
874 - def _children(self):
875 return self._data
876
877 -class AbstractNIContainer(AbstractContainer):
878 """ 879 Same as the L{AbstractContainer}, but provides access to the child elements 880 through C{AbstractNativeIndexer._getinternal} instead of the standard 881 __getitem__. 882 883 Therefore, the C{self._children} property must return an object which 884 implements L{AbstractIndexer}. 885 """
886 - def __getitem__(self, i):
887 return self._children._getinternal(i)
888 889 try: 890 from collections import OrderedDict as _dict
891 - class OrderedDict(_dict):
892 pass
893 except ImportError: 894 import UserDict
895 896 - class OrderedDict(dict, UserDict.DictMixin):
897
898 - def __init__(self, *args, **kwds):
899 if len(args) > 1: 900 raise TypeError('expected at most 1 arguments, got {0}'.format(len(args))) 901 try: 902 self.__end 903 except AttributeError: 904 self.clear() 905 self.update(*args, **kwds)
906
907 - def clear(self):
908 self.__end = end = [] 909 end += [None, end, end] # sentinel node for doubly linked list 910 self.__map = {} # key --> [key, prev, next] 911 dict.clear(self)
912
913 - def __setitem__(self, key, value):
914 if key not in self: 915 end = self.__end 916 curr = end[1] 917 curr[2] = end[1] = self.__map[key] = [key, curr, end] 918 dict.__setitem__(self, key, value)
919
920 - def __delitem__(self, key):
921 dict.__delitem__(self, key) 922 key, prev, next = self.__map.pop(key) 923 prev[2] = next 924 next[1] = prev
925
926 - def __iter__(self):
927 end = self.__end 928 curr = end[2] 929 while curr is not end: 930 yield curr[0] 931 curr = curr[2]
932
933 - def __reversed__(self):
934 end = self.__end 935 curr = end[1] 936 while curr is not end: 937 yield curr[0] 938 curr = curr[1]
939
940 - def popitem(self, last=True):
941 if not self: 942 raise KeyError('dictionary is empty') 943 if last: 944 key = next(reversed(self)) 945 else: 946 key = next(iter(self)) 947 value = self.pop(key) 948 return key, value
949
950 - def __reduce__(self):
951 items = [[k, self[k]] for k in self] 952 tmp = self.__map, self.__end 953 del self.__map, self.__end 954 inst_dict = vars(self).copy() 955 self.__map, self.__end = tmp 956 if inst_dict: 957 return (self.__class__, (items,), inst_dict) 958 return self.__class__, (items,)
959
960 - def keys(self):
961 return list(self)
962 963 setdefault = UserDict.DictMixin.setdefault 964 update = UserDict.DictMixin.update 965 pop = UserDict.DictMixin.pop 966 values = UserDict.DictMixin.values 967 items = UserDict.DictMixin.items 968 iterkeys = UserDict.DictMixin.iterkeys 969 itervalues = UserDict.DictMixin.itervalues 970 iteritems = UserDict.DictMixin.iteritems 971
972 - def __repr__(self):
973 if not self: 974 return '{0}()'.format(self.__class__.__name__) 975 return '{0}({1!r})'.format(self.__class__.__name__, self.items())
976
977 - def copy(self):
978 return self.__class__(self)
979 980 @classmethod
981 - def fromkeys(cls, iterable, value=None):
982 d = cls() 983 for key in iterable: 984 d[key] = value 985 return d
986
987 - def __eq__(self, other):
988 if isinstance(other, OrderedDict): 989 return len(self)==len(other) and self.items() == other.items() 990 return dict.__eq__(self, other)
991
992 - def __ne__(self, other):
993 return not self == other
994