Repository: jsonpickle/jsonpickle
Pull request #252 (2019-03-03 01:14:41)

Make base85 opt-in instead of default
Change addresses performance concerns detailed in #251.


Patch:
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
index bd07c69..fa4357e 100644
--- a/docs/source/changelog.rst
+++ b/docs/source/changelog.rst
@@ -9,14 +9,15 @@ Version 1.2 - TBD 2019
       These objects are now handled robustly.
       (`#247 <https://github.com/jsonpickle/jsonpickle/issues/247>`_).
 
-    * `Base85`_ is now the default binary data encoding on Python 3, as it's
-      more space-efficient than base64. Python 3 can still read and write
-      base64-encoded data, but Python 2 cannot read nor write base85. For
-      backwards compatibility, the pickler can be configured to encode using
-      only base64 via the new ``use_base85`` argument.
+    * Support for encoding binary data in `base85`_ instead of base64 has been
+      added on Python 3. Base85 produces payloads about 10% smaller than base64,
+      albeit at the cost of lower throughput. For performance and backwards
+      compatibility with Python 2 the pickler uses base64 by default, but it can
+      be configured to only use ``base85`` with the new ``use_base85`` argument.
+
       (`#251 <https://github.com/jsonpickle/jsonpickle/issues/251>`_).
 
-.. _Base85: https://en.wikipedia.org/wiki/Ascii85
+.. _base85: https://en.wikipedia.org/wiki/Ascii85
 
 Version 1.1 - January 22, 2019
 ------------------------------
diff --git a/jsonpickle/pickler.py b/jsonpickle/pickler.py
index 618d026..63847f2 100644
--- a/jsonpickle/pickler.py
+++ b/jsonpickle/pickler.py
@@ -31,7 +31,7 @@ def encode(value,
            max_iter=None,
            use_decimal=False,
            numeric_keys=False,
-           use_base85=True):
+           use_base85=False):
     """Return a JSON formatted representation of value, a Python object.
 
     :param unpicklable: If set to False then the output will not contain the
@@ -79,8 +79,7 @@ def encode(value,
     :param use_base85:
         If possible, use base85 to encode binary data. Base85 bloats binary data
         by 1/4 as opposed to base64, which expands it by 1/3. This argument is
-        forced to False on Python 2 because it doesn't support it; base64 will
-        be used instead.
+        ignored on Python 2 because it doesn't support it.
     """
     backend = backend or json
     context = context or Pickler(
@@ -109,7 +108,7 @@ class Pickler(object):
                  max_iter=None,
                  numeric_keys=False,
                  use_decimal=False,
-                 use_base85=True):
+                 use_base85=False):
         self.unpicklable = unpicklable
         self.make_refs = make_refs
         self.backend = backend or json


Test patch:
diff --git a/tests/jsonpickle_test.py b/tests/jsonpickle_test.py
index 0213637..6e41384 100644
--- a/tests/jsonpickle_test.py
+++ b/tests/jsonpickle_test.py
@@ -98,6 +98,7 @@ class PicklingTestCase(unittest.TestCase):
     def setUp(self):
         self.pickler = jsonpickle.pickler.Pickler()
         self.unpickler = jsonpickle.unpickler.Unpickler()
+        self.b85_pickler = jsonpickle.pickler.Pickler(use_base85=True)
 
     def tearDown(self):
         self.pickler.reset()
@@ -105,33 +106,24 @@ class PicklingTestCase(unittest.TestCase):
 
     @unittest.skipIf(not PY2, 'Python 2-specific base85 test')
     def test_base85_always_false_on_py2(self):
-        pickler = jsonpickle.pickler.Pickler(use_base85=True)
-        self.assertFalse(pickler.use_base85)
-
-    @unittest.skipIf(PY2, 'Base85 not supported on Python 2')
-    def test_base85_default_py3(self):
-        """Ensure Python 2 allows use_base85 as default on Python 3"""
-        pickler = jsonpickle.pickler.Pickler()
-        self.assertTrue(pickler.use_base85)
+        self.assertFalse(self.b85_pickler.use_base85)
 
     @unittest.skipIf(PY2, 'Base85 not supported on Python 2')
     def test_base85_override_py3(self):
         """Ensure the Python 2 check still lets us set use_base85 on Python 3"""
-        pickler = jsonpickle.pickler.Pickler(use_base85=False)
-        self.assertFalse(pickler.use_base85)
+        self.assertTrue(self.b85_pickler.use_base85)
 
     @unittest.skipIf(PY2, 'Base85 not supported on Python 2')
     def test_bytes_default_base85(self):
         data = os.urandom(16)
         encoded = util.b85encode(data)
-        self.assertEqual({tags.B85: encoded}, self.pickler.flatten(data))
+        self.assertEqual({tags.B85: encoded}, self.b85_pickler.flatten(data))
 
     @unittest.skipIf(PY2, 'Base85 not supported on Python 2')
-    def test_py3_bytes_base64_override(self):
-        pickler = jsonpickle.pickler.Pickler(use_base85=False)
+    def test_py3_bytes_base64_default(self):
         data = os.urandom(16)
         encoded = util.b64encode(data)
-        self.assertEqual({tags.B64: encoded}, pickler.flatten(data))
+        self.assertEqual({tags.B64: encoded}, self.pickler.flatten(data))
 
     @unittest.skipIf(not PY2, 'Python 2-specific base64 test')
     def test_py2_default_base64(self):
diff --git a/tests/object_test.py b/tests/object_test.py
index 76f0340..9444988 100644
--- a/tests/object_test.py
+++ b/tests/object_test.py
@@ -11,17 +11,10 @@ from jsonpickle import compat
 from jsonpickle import handlers
 from jsonpickle import tags
 from jsonpickle import util
-from jsonpickle.compat import queue, PY2, encodebytes
+from jsonpickle.compat import queue, PY2
 
 from helper import SkippableTest
 
-if PY2:
-    default_bytes_encoder = util.b64encode
-    default_bytes_tag = tags.B64
-else:
-    default_bytes_encoder = util.b85encode
-    default_bytes_tag = tags.B85
-
 
 class Thing(object):
 
@@ -816,9 +809,9 @@ class AdvancedObjectsTestCase(SkippableTest):
             self.assertTrue(isinstance(encoded, compat.ustr))
         else:
             self.assertNotEqual(encoded, u1)
-            encoded_ustr = default_bytes_encoder(b'foo')
-            self.assertEqual({default_bytes_tag: encoded_ustr}, encoded)
-            self.assertTrue(isinstance(encoded[default_bytes_tag], compat.ustr))
+            encoded_ustr = util.b64encode(b'foo')
+            self.assertEqual({tags.B64: encoded_ustr}, encoded)
+            self.assertTrue(isinstance(encoded[tags.B64], compat.ustr))
         decoded = self.unpickler.restore(encoded)
         self.assertTrue(decoded == b1)
         if PY2:
@@ -829,9 +822,9 @@ class AdvancedObjectsTestCase(SkippableTest):
         # bytestrings that we can't decode to UTF-8 will always be wrapped
         encoded = self.pickler.flatten(b2)
         self.assertNotEqual(encoded, b2)
-        encoded_ustr = default_bytes_encoder(b'foo\xff')
-        self.assertEqual({default_bytes_tag: encoded_ustr}, encoded)
-        self.assertTrue(isinstance(encoded[default_bytes_tag], compat.ustr))
+        encoded_ustr = util.b64encode(b'foo\xff')
+        self.assertEqual({tags.B64: encoded_ustr}, encoded)
+        self.assertTrue(isinstance(encoded[tags.B64], compat.ustr))
         decoded = self.unpickler.restore(encoded)
         self.assertEqual(decoded, b2)
         self.assertTrue(isinstance(decoded, bytes))