[or-cvs] r17104: {updater} Stop trying to monkeypatch simplejson to support canonical e (updater/trunk/lib/thandy)

nickm at seul.org nickm at seul.org
Tue Oct 14 21:07:57 UTC 2008


Author: nickm
Date: 2008-10-14 17:07:57 -0400 (Tue, 14 Oct 2008)
New Revision: 17104

Modified:
   updater/trunk/lib/thandy/formats.py
Log:
Stop trying to monkeypatch simplejson to support canonical encoding, and just use our own internal canonical encoder when we want that.  It is actually less code this way.

Modified: updater/trunk/lib/thandy/formats.py
===================================================================
--- updater/trunk/lib/thandy/formats.py	2008-10-14 21:05:54 UTC (rev 17103)
+++ updater/trunk/lib/thandy/formats.py	2008-10-14 21:07:57 UTC (rev 17104)
@@ -153,60 +153,53 @@
 
     return SignatureStatus(goodSigs, badSigs, unknownSigs, tangentialSigs)
 
-def _encodeCanonical_makeiter(obj):
-    """Return an iterator to encode 'obj' canonically, and a nil
-       cleanup function.  Works with newer versions of simplejson that
-       have a _make_iterencode method.
-    """
-    def default(o):
-        raise TypeError("Can't encode %r", o)
-    def floatstr(o):
-        raise TypeError("Floats not allowed.")
-    def canonical_str_encoder(s):
-        return '"%s"' % re.sub(r'(["\\])', r'\\\1', s)
+def _encodeCanonical(obj, outf):
+    # Helper for encodeCanonical.  Older versions of simplejson.encoder don't
+    # even let us replace the separators.
 
-    # XXX This is, alas, a hack.  I'll submit a canonical JSon patch to
-    # the simplejson folks.
-    iterator = simplejson.encoder._make_iterencode(
-        None, default, canonical_str_encoder, None, floatstr,
-        ":", ",", True, False, True)(obj, 0)
-
-    return iterator, lambda:None
-
-def _encodeCanonical_monkeypatch(obj):
-    """Return an iterator to encode 'obj' canonically, and a cleanup
-       function to un-monkeypatch simplejson.  Works with older
-       versions of simplejson.  This is not threadsafe wrt other
-       invocations of simplejson, so until we're all upgraded, no
-       doing canonical encodings outside of the main thread.
-    """
-    def default(o):
-        raise TypeError("Can't encode %r", o)
-    save_floatstr = simplejson.encoder.floatstr
-    save_encode_basestring = simplejson.encoder.encode_basestring
-    def floatstr(o):
-        raise TypeError("Floats not allowed.")
     def canonical_str_encoder(s):
-        return '"%s"' % re.sub(r'(["\\])', r'\\\1', s)
-    simplejson.encoder.floatstr = floatstr
-    simplejson.encoder.encode_basestring = canonical_str_encoder
-    def unpatch():
-        simplejson.encoder.floatstr = save_floatstr
-        simplejson.encoder.encode_basestring = save_encode_basestring
+        s = '"%s"' % re.sub(r'(["\\])', r'\\\1', s)
+        if isinstance(s, unicode):
+            return s.encode("utf-8")
+        else:
+            return s
 
-    encoder = simplejson.encoder.JSONEncoder(ensure_ascii=False,
-                                             check_circular=False,
-                                             allow_nan=False,
-                                             sort_keys=True,
-                                             separators=(",",":"),
-                                             default=default)
-    return encoder.iterencode(obj), unpatch
+    if isinstance(obj, basestring):
+        outf(canonical_str_encoder(obj))
+    elif obj is True:
+        outf("true")
+    elif obj is False:
+            outf("false")
+    elif obj is None:
+        outf("null")
+    elif isinstance(obj, (int,long)):
+        outf(str(obj))
+    elif isinstance(obj, (tuple, list)):
+        outf("[")
+        if len(obj):
+            for item in obj[:-1]:
+                _encodeCanonical(item, outf)
+                outf(",")
+            _encodeCanonical(obj[-1], outf)
+        outf("]")
+    elif isinstance(obj, dict):
+        outf("{")
+        if len(obj):
+            items = obj.items()
+            items.sort()
+            for k,v in items[:-1]:
+                outf(canonical_str_encoder(k))
+                outf(":")
+                _encodeCanonical(v, outf)
+                outf(",")
+            k, v = items[-1]
+            outf(canonical_str_encoder(k))
+            outf(":")
+            _encodeCanonical(v, outf)
+        outf("}")
+    else:
+        raise thandy.FormatException("I can't encode %r"%obj)
 
-if hasattr(simplejson.encoder, "_make_iterencode"):
-    _encodeCanonical = _encodeCanonical_makeiter
-else:
-    _encodeCanonical = _encodeCanonical_monkeypatch
-
 def encodeCanonical(obj, outf=None):
     """Encode the object obj in canoncial JSon form, as specified at
        http://wiki.laptop.org/go/Canonical_JSON .  It's a restricted
@@ -220,6 +213,10 @@
        '""'
        >>> encodeCanonical([1, 2, 3])
        '[1,2,3]'
+       >>> encodeCanonical([])
+       '[]'
+       >>> encodeCanonical({"A": [99]})
+       '{"A":[99]}'
        >>> encodeCanonical({"x" : 3, "y" : 2})
        '{"x":3,"y":2}'
     """
@@ -229,16 +226,12 @@
         result = [ ]
         outf = result.append
 
-    iterator, cleanup = _encodeCanonical(obj)
+    _encodeCanonical(obj, outf)
 
-    try:
-        for u in iterator:
-            outf(u.encode("utf-8"))
-    finally:
-        cleanup()
     if result is not None:
         return "".join(result)
 
+
 def getDigest(obj, digestObj=None):
     """Update 'digestObj' (typically a SHA256 object) with the digest of
        the canonical json encoding of obj.  If digestObj is none,



More information about the tor-commits mailing list