[tor-commits] [stem/master] OrderedDict substitute for older python versions

atagar at torproject.org atagar at torproject.org
Mon Oct 29 16:04:29 UTC 2012


commit b09de1093e7a7237d3825d5728126cbf2f15842b
Author: Damian Johnson <atagar at torproject.org>
Date:   Mon Oct 29 08:47:26 2012 -0700

    OrderedDict substitute for older python versions
    
    In d30a628e I simplified the _get_descriptor_components() by using the
    collections.OrderedDict builtin. Unforutunately OrderedDict was introduced in
    python 2.7, breaking our 2.5 and 2.6 compatability...
    
    ======================================================================
    ERROR: test_voting_delay
    ----------------------------------------------------------------------
    Traceback:
      File "/home/atagar/Desktop/stem/test/unit/descriptor/networkstatus/document_v3.py", line 359, in test_voting_delay
        document = get_network_status_document_v3({"voting-delay": "12 345"})
      File "/home/atagar/Desktop/stem/test/mocking.py", line 782, in get_network_status_document_v3
        return stem.descriptor.networkstatus.NetworkStatusDocumentV3(desc_content, validate = True)
      File "/home/atagar/Desktop/stem/stem/descriptor/networkstatus.py", line 458, in __init__
        self._header = _DocumentHeader(document_file, validate, default_params)
      File "/home/atagar/Desktop/stem/stem/descriptor/networkstatus.py", line 541, in __init__
        entries = stem.descriptor._get_descriptor_components(content, validate)
      File "/home/atagar/Desktop/stem/stem/descriptor/__init__.py", line 289, in _get_descriptor_components
        entries = collections.OrderedDict()
    AttributeError: 'module' object has no attribute 'OrderedDict'
    
    Caught by eoinof on...
    
    https://trac.torproject.org/7244
    
    I'm fixing this by adding an OrderedDict substitute that's under the MIT
    license...
    
    http://pypi.python.org/pypi/ordereddict
---
 stem/descriptor/__init__.py |    8 ++-
 stem/util/__init__.py       |    1 +
 stem/util/ordereddict.py    |  130 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 137 insertions(+), 2 deletions(-)

diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index ff3d59f..d667946 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -25,10 +25,14 @@ __all__ = [
 
 import os
 import re
-import collections
 
 import stem.util.enum
 
+try:
+  from collections import OrderedDict
+except ImportError:
+  from stem.util.ordereddict import OrderedDict
+
 KEYWORD_CHAR    = "a-zA-Z0-9-"
 WHITESPACE      = " \t"
 KEYWORD_LINE    = re.compile("^([%s]+)(?:[%s]+(.*))?$" % (KEYWORD_CHAR, WHITESPACE))
@@ -286,7 +290,7 @@ def _get_descriptor_components(raw_contents, validate, extra_keywords = ()):
     value tuple, the second being a list of those entries.
   """
   
-  entries = collections.OrderedDict()
+  entries = OrderedDict()
   extra_entries = [] # entries with a keyword in extra_keywords
   remaining_lines = raw_contents.split("\n")
   
diff --git a/stem/util/__init__.py b/stem/util/__init__.py
index 78fe9e1..d371629 100644
--- a/stem/util/__init__.py
+++ b/stem/util/__init__.py
@@ -7,6 +7,7 @@ __all__ = [
   "connection",
   "enum",
   "log",
+  "ordereddict",
   "proc",
   "system",
   "term",
diff --git a/stem/util/ordereddict.py b/stem/util/ordereddict.py
new file mode 100644
index 0000000..cb97874
--- /dev/null
+++ b/stem/util/ordereddict.py
@@ -0,0 +1,130 @@
+# Drop in replacement for python 2.7's OrderedDict, from...
+# http://pypi.python.org/pypi/ordereddict
+
+# Copyright (c) 2009 Raymond Hettinger
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+#     The above copyright notice and this permission notice shall be
+#     included in all copies or substantial portions of the Software.
+#
+#     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+#     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+#     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+#     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+#     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+#     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+#     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+#     OTHER DEALINGS IN THE SOFTWARE.
+
+from UserDict import DictMixin
+
+class OrderedDict(dict, DictMixin):
+  def __init__(self, *args, **kwds):
+    if len(args) > 1:
+      raise TypeError('expected at most 1 arguments, got %d' % len(args))
+    try:
+      self.__end
+    except AttributeError:
+      self.clear()
+    self.update(*args, **kwds)
+  
+  def clear(self):
+    self.__end = end = []
+    end += [None, end, end]         # sentinel node for doubly linked list
+    self.__map = {}                 # key --> [key, prev, next]
+    dict.clear(self)
+  
+  def __setitem__(self, key, value):
+    if key not in self:
+      end = self.__end
+      curr = end[1]
+      curr[2] = end[1] = self.__map[key] = [key, curr, end]
+    dict.__setitem__(self, key, value)
+  
+  def __delitem__(self, key):
+    dict.__delitem__(self, key)
+    key, prev, next = self.__map.pop(key)
+    prev[2] = next
+    next[1] = prev
+  
+  def __iter__(self):
+    end = self.__end
+    curr = end[2]
+    while curr is not end:
+      yield curr[0]
+      curr = curr[2]
+  
+  def __reversed__(self):
+    end = self.__end
+    curr = end[1]
+    while curr is not end:
+      yield curr[0]
+      curr = curr[1]
+  
+  def popitem(self, last=True):
+    if not self:
+      raise KeyError('dictionary is empty')
+    if last:
+      key = reversed(self).next()
+    else:
+      key = iter(self).next()
+    value = self.pop(key)
+    return key, value
+  
+  def __reduce__(self):
+    items = [[k, self[k]] for k in self]
+    tmp = self.__map, self.__end
+    del self.__map, self.__end
+    inst_dict = vars(self).copy()
+    self.__map, self.__end = tmp
+    if inst_dict:
+      return (self.__class__, (items,), inst_dict)
+    return self.__class__, (items,)
+  
+  def keys(self):
+    return list(self)
+  
+  setdefault = DictMixin.setdefault
+  update = DictMixin.update
+  pop = DictMixin.pop
+  values = DictMixin.values
+  items = DictMixin.items
+  iterkeys = DictMixin.iterkeys
+  itervalues = DictMixin.itervalues
+  iteritems = DictMixin.iteritems
+  
+  def __repr__(self):
+    if not self:
+      return '%s()' % (self.__class__.__name__,)
+    return '%s(%r)' % (self.__class__.__name__, self.items())
+  
+  def copy(self):
+    return self.__class__(self)
+  
+  @classmethod
+  def fromkeys(cls, iterable, value=None):
+    d = cls()
+    for key in iterable:
+      d[key] = value
+    return d
+  
+  def __eq__(self, other):
+    if isinstance(other, OrderedDict):
+      if len(self) != len(other):
+        return False
+      for p, q in  zip(self.items(), other.items()):
+        if p != q:
+          return False
+      return True
+    return dict.__eq__(self, other)
+  
+  def __ne__(self, other):
+    return not self == other
+





More information about the tor-commits mailing list