Hello community,

here is the log from the commit of package python-serpent for 
openSUSE:Leap:15.2 checked in at 2020-05-19 14:09:06
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Leap:15.2/python-serpent (Old)
 and      /work/SRC/openSUSE:Leap:15.2/.python-serpent.new.2738 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-serpent"

Tue May 19 14:09:06 2020 rev:7 rq:807102 version:1.28

Changes:
--------
--- /work/SRC/openSUSE:Leap:15.2/python-serpent/python-serpent.changes  
2020-04-30 18:51:44.364636362 +0200
+++ 
/work/SRC/openSUSE:Leap:15.2/.python-serpent.new.2738/python-serpent.changes    
    2020-05-19 14:09:06.583071960 +0200
@@ -2,12 +1,0 @@
-Wed Mar 18 13:31:41 UTC 2020 - [email protected]
-
-- version update to 1.30.2
-  * upstream does not support python 2 anymore
-  * no changelog found
-
--------------------------------------------------------------------
-Sat Mar 14 07:12:14 UTC 2020 - Tomáš Chvátal <[email protected]>
-
-- Fix building without python2
-
--------------------------------------------------------------------

Old:
----
  serpent-1.30.2.tar.gz

New:
----
  serpent-1.28.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-serpent.spec ++++++
--- /var/tmp/diff_new_pack.djHGSb/_old  2020-05-19 14:09:06.919072613 +0200
+++ /var/tmp/diff_new_pack.djHGSb/_new  2020-05-19 14:09:06.927072629 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-serpent
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,23 +17,27 @@
 
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
-%define skip_python2 1
 Name:           python-serpent
-Version:        1.30.2
+Version:        1.28
 Release:        0
 Summary:        Serialization based on astliteral_eval
 License:        MIT
+Group:          Development/Languages/Python
 URL:            https://github.com/irmen/Serpent
 Source:         
https://files.pythonhosted.org/packages/source/s/serpent/serpent-%{version}.tar.gz
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
+BuildRequires:  python-enum34
 BuildRequires:  python-rpm-macros
-BuildArch:      noarch
 # SECTION test requirements
 BuildRequires:  %{python_module attrs}
 BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module pytz}
 # /SECTION
+BuildArch:      noarch
+%ifpython2
+Requires:       python-enum34
+%endif
 %python_subpackages
 
 %description

++++++ serpent-1.30.2.tar.gz -> serpent-1.28.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/PKG-INFO new/serpent-1.28/PKG-INFO
--- old/serpent-1.30.2/PKG-INFO 2020-02-01 10:36:38.472208500 +0100
+++ new/serpent-1.28/PKG-INFO   2019-03-10 17:33:49.000000000 +0100
@@ -1,8 +1,8 @@
-Metadata-Version: 1.2
+Metadata-Version: 1.1
 Name: serpent
-Version: 1.30.2
+Version: 1.28
 Summary: Serialization based on ast.literal_eval
-Home-page: https://github.com/irmen/Serpent
+Home-page: UNKNOWN
 Author: Irmen de Jong
 Author-email: [email protected]
 License: MIT
@@ -21,7 +21,7 @@
         
         **API**
         
-        - ``ser_bytes = serpent.dumps(obj, indent=False, 
module_in_classname=False):``      # serialize obj tree to bytes
+        - ``ser_bytes = serpent.dumps(obj, indent=False, set_literals=True, 
module_in_classname=False):``      # serialize obj tree to bytes
         - ``obj = serpent.loads(ser_bytes)``     # deserialize bytes back into 
object tree
         - You can use ``ast.literal_eval`` yourself to deserialize, but 
``serpent.deserialize``
           works around a few corner cases. See source for details.
@@ -41,7 +41,7 @@
         Serpent allows comments in the serialized data (because it is just 
Python source code).
         Serpent can't serialize object graphs (when an object refers to 
itself); it will then crash with a ValueError pointing out the problem.
         
-        Works with Python 3.5+
+        Works with Python 2.7+ (including 3.x), IronPython 2.7+, Jython 2.7+.
         
         **FAQ**
         
@@ -63,7 +63,9 @@
         
         .. code:: python
         
+         # This demo script is written for Python 3.2+
          # -*- coding: utf-8 -*-
+         from __future__ import print_function
          import ast
          import uuid
          import datetime
@@ -105,7 +107,7 @@
          assert data2==data
         
         
-        When you run this it prints:
+        When you run this (with python 3.2+) it prints:
         
         .. code:: python
         
@@ -148,9 +150,9 @@
 Classifier: Natural Language :: English
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
 Classifier: Topic :: Software Development
-Requires-Python: >=3.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/README.md new/serpent-1.28/README.md
--- old/serpent-1.30.2/README.md        2020-01-25 15:33:28.000000000 +0100
+++ new/serpent-1.28/README.md  2018-08-16 09:49:54.000000000 +0200
@@ -24,8 +24,7 @@
 
 PYTHON
 ------
-Compatible with Python 3.5+ (use a serpent version before 1.30 for Python 2.7 
support)
-It can be found on Pypi as 'serpent': https://pypi.python.org/pypi/serpent
+Package can be found on Pypi as 'serpent': https://pypi.python.org/pypi/serpent
 Example usage can be found in ./tests/example.py
 
 
@@ -48,23 +47,31 @@
 
 SOME MORE DETAILS
 -----------------
+Compatible with Python 2.7+ (including 3.x), IronPython 2.7+, Jython 2.7+.
+
 Serpent handles several special Python types to make life easier:
 
- - bytes, bytearrays, memoryview --> string, base-64
+ - str  --> promoted to unicode (see below why this is)
+ - bytes, bytearrays, memoryview, buffer  --> string, base-64
    (you'll have to manually un-base64 them though. Can use serpent.tobytes 
function)
  - uuid.UUID, datetime.{datetime, date, time, timespan}  --> appropriate 
string/number
  - decimal.Decimal  --> string (to not lose precision)
- - array.array typecode 'u' --> string
+ - array.array typecode 'c'/'u' --> string/unicode
  - array.array other typecode --> list
  - Exception  --> dict with some fields of the exception (message, args)
  - collections module types  --> mostly equivalent primitive types or dict
- - enums --> the value of the enum
+ - enums --> the value of the enum (Python 3.4+ or enum34 library)
  - namedtuple --> treated as just a tuple
  - attr dataclasses and python 3.7 native dataclasses: treated as just a 
class, so will become a dict
- - all other types  --> dict with the ``__getstate__`` or ``vars()`` of the 
object, and a ``__class__`` element with the name of the class
+ - all other types  --> dict with the ``__getstate__`` or ``vars()`` of the 
object, and a ``__class__`` element with the name of the class 
 
 Notes:
 
+All str will be promoted to unicode. This is done because it is the
+default anyway for Python 3.x, and it solves the problem of the str/unicode
+difference between different Python versions. Also it means the serialized
+output doesn't have those problematic 'u' prefixes on strings.
+
 The serializer is not thread-safe. Make sure you're not making changes
 to the object tree that is being serialized, and don't use the same
 serializer in different threads.
@@ -73,7 +80,19 @@
 contain comments. Serpent does not add comments by itself apart from the
 single header line.
 
+Set literals are not supported on python <3.2 (``ast.literal_eval``
+limitation). If you need Python < 3.2 compatibility, you'll have to use
+``set_literals=False`` when serializing. Since version 1.6 serpent chooses
+this wisely for you by default, but you can still override it if needed.
+
 Floats +inf and -inf are handled via a trick, Float 'nan' cannot be handled
 and is represented by the special value:  
``{'__class__':'float','value':'nan'}``
 We chose not to encode it as just the string 'NaN' because that could cause
 memory issues when used in multiplications.
+
+Jython's ast module cannot properly parse some literal reprs of unicode 
strings.
+This is a known bug http://bugs.jython.org/issue2008
+It seems to work when your server is Python 2.x but safest is perhaps to make
+sure your data to parse contains only ascii strings when dealing with Jython.
+Serpent checks for possible problems and will raise an error if it finds one,
+rather than continuing with string data that might be incorrect.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/serpent.egg-info/PKG-INFO 
new/serpent-1.28/serpent.egg-info/PKG-INFO
--- old/serpent-1.30.2/serpent.egg-info/PKG-INFO        2020-02-01 
10:36:38.000000000 +0100
+++ new/serpent-1.28/serpent.egg-info/PKG-INFO  2019-03-10 17:33:49.000000000 
+0100
@@ -1,8 +1,8 @@
-Metadata-Version: 1.2
+Metadata-Version: 1.1
 Name: serpent
-Version: 1.30.2
+Version: 1.28
 Summary: Serialization based on ast.literal_eval
-Home-page: https://github.com/irmen/Serpent
+Home-page: UNKNOWN
 Author: Irmen de Jong
 Author-email: [email protected]
 License: MIT
@@ -21,7 +21,7 @@
         
         **API**
         
-        - ``ser_bytes = serpent.dumps(obj, indent=False, 
module_in_classname=False):``      # serialize obj tree to bytes
+        - ``ser_bytes = serpent.dumps(obj, indent=False, set_literals=True, 
module_in_classname=False):``      # serialize obj tree to bytes
         - ``obj = serpent.loads(ser_bytes)``     # deserialize bytes back into 
object tree
         - You can use ``ast.literal_eval`` yourself to deserialize, but 
``serpent.deserialize``
           works around a few corner cases. See source for details.
@@ -41,7 +41,7 @@
         Serpent allows comments in the serialized data (because it is just 
Python source code).
         Serpent can't serialize object graphs (when an object refers to 
itself); it will then crash with a ValueError pointing out the problem.
         
-        Works with Python 3.5+
+        Works with Python 2.7+ (including 3.x), IronPython 2.7+, Jython 2.7+.
         
         **FAQ**
         
@@ -63,7 +63,9 @@
         
         .. code:: python
         
+         # This demo script is written for Python 3.2+
          # -*- coding: utf-8 -*-
+         from __future__ import print_function
          import ast
          import uuid
          import datetime
@@ -105,7 +107,7 @@
          assert data2==data
         
         
-        When you run this it prints:
+        When you run this (with python 3.2+) it prints:
         
         .. code:: python
         
@@ -148,9 +150,9 @@
 Classifier: Natural Language :: English
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.4
 Classifier: Programming Language :: Python :: 3.5
 Classifier: Programming Language :: Python :: 3.6
 Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
 Classifier: Topic :: Software Development
-Requires-Python: >=3.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/serpent.py new/serpent-1.28/serpent.py
--- old/serpent-1.30.2/serpent.py       2020-02-01 10:26:12.000000000 +0100
+++ new/serpent-1.28/serpent.py 2019-03-10 17:23:39.000000000 +0100
@@ -7,23 +7,29 @@
 machines over the network for instance (because only 'safe' literals are
 encoded).
 
-Compatible with Python 3.5+
+Compatible with Python 2.7+ (including 3.x), IronPython 2.7+, Jython 2.7+.
 
 Serpent handles several special Python types to make life easier:
 
- - bytes, bytearrays, memoryview --> string, base-64
+ - str  --> promoted to unicode (see below why this is)
+ - bytes, bytearrays, memoryview, buffer  --> string, base-64
    (you'll have to manually un-base64 them though)
  - uuid.UUID, datetime.{datetime, date, time, timespan}  --> appropriate 
string/number
  - decimal.Decimal  --> string (to not lose precision)
- - array.array typecode 'u' --> string
+ - array.array typecode 'c'/'u' --> string/unicode
  - array.array other typecode --> list
  - Exception  --> dict with some fields of the exception (message, args)
  - collections module types  --> mostly equivalent primitive types or dict
- - enums --> the value of the enum
+ - enums --> the value of the enum (Python 3.4+ or enum34 library)
  - all other types  --> dict with  __getstate__  or vars() of the object
 
 Notes:
 
+All str will be promoted to unicode. This is done because it is the
+default anyway for Python 3.x, and it solves the problem of the str/unicode
+difference between different Python versions. Also it means the serialized
+output doesn't have those problematic 'u' prefixes on strings.
+
 The serializer is not thread-safe. Make sure you're not making changes
 to the object tree that is being serialized, and don't use the same
 serializer in different threads.
@@ -31,18 +37,34 @@
 Because the serialized format is just valid Python source code, it can
 contain comments.
 
+Set literals are not supported on python <3.2 (ast.literal_eval
+limitation). If you need Python < 3.2 compatibility, you'll have to use
+set_literals=False when serializing. Since version 1.6 serpent chooses
+this wisely for you by default, but you can still override it if needed.
+
 Floats +inf and -inf are handled via a trick, Float 'nan' cannot be handled
 and is represented by the special value:  {'__class__':'float','value':'nan'}
 We chose not to encode it as just the string 'NaN' because that could cause
 memory issues when used in multiplications.
 
+Jython's ast module cannot properly parse some literal reprs of unicode 
strings.
+This is a known bug http://bugs.jython.org/issue2008
+It seems to work when your server is Python 2.x but safest is perhaps to make
+sure your data to parse contains only ascii strings when dealing with Jython.
+Serpent checks for possible problems and will raise an error if it finds one,
+rather than continuing with string data that might be incorrect.
+
 Copyright by Irmen de Jong ([email protected])
 Software license: "MIT software license". See 
http://opensource.org/licenses/MIT
 """
 
+from __future__ import print_function, division
+import __future__
 import ast
 import base64
 import sys
+import types
+import os
 import gc
 import decimal
 import datetime
@@ -52,31 +74,65 @@
 import numbers
 import codecs
 import collections
-import enum
-from collections.abc import KeysView, ValuesView, ItemsView
+if sys.version_info >= (3, 4):
+    from collections.abc import KeysView, ValuesView, ItemsView
+    import enum
+else:
+    from collections import KeysView, ValuesView, ItemsView
+    try:
+        import enum
+    except ImportError:
+        enum = None
 
 
-__version__ = "1.30.2"
+__version__ = "1.28"
 __all__ = ["dump", "dumps", "load", "loads", "register_class", 
"unregister_class", "tobytes"]
 
+can_use_set_literals = sys.version_info >= (3, 2)  # check if we can use set 
literals
+
 
-def dumps(obj, indent=False, module_in_classname=False):
+def dumps(obj, indent=False, set_literals=can_use_set_literals, 
module_in_classname=False):
     """Serialize object tree to bytes"""
-    return Serializer(indent, module_in_classname).serialize(obj)
+    return Serializer(indent, set_literals, module_in_classname).serialize(obj)
 
 
-def dump(obj, file, indent=False, module_in_classname=False):
+def dump(obj, file, indent=False, set_literals=can_use_set_literals, 
module_in_classname=False):
     """Serialize object tree to a file"""
-    file.write(dumps(obj, indent=indent, 
module_in_classname=module_in_classname))
+    file.write(dumps(obj, indent=indent, set_literals=set_literals, 
module_in_classname=module_in_classname))
 
 
 def loads(serialized_bytes):
     """Deserialize bytes back to object tree. Uses ast.literal_eval (safe)."""
-    serialized = codecs.decode(serialized_bytes, "utf-8")
+    if os.name == "java":
+        if type(serialized_bytes) is memoryview:
+            serialized_bytes = serialized_bytes.tobytes()
+        elif type(serialized_bytes) is buffer:
+            serialized_bytes = serialized_bytes[:]
+        serialized = serialized_bytes.decode("utf-8")
+    elif sys.platform == "cli":
+        if type(serialized_bytes) is memoryview:
+            serialized_bytes = serialized_bytes.tobytes()
+        serialized = codecs.decode(serialized_bytes, "utf-8")
+    else:
+        serialized = codecs.decode(serialized_bytes, "utf-8")
     if '\x00' in serialized:
         raise ValueError("The serpent data contains 0-bytes so it cannot be 
parsed by ast.literal_eval. Has it been corrupted?")
+    if sys.version_info < (3, 0):
+        # python 2.x: parse with unicode_literals (promotes all strings to 
unicode)
+        # note: this doesn't work on jython... see bug 
http://bugs.jython.org/issue2008
+        # so we add a safety net, to avoid working with incorrectly processed 
unicode strings
+        serialized = compile(serialized, "<serpent>", mode="eval", 
flags=ast.PyCF_ONLY_AST | __future__.unicode_literals.compiler_flag)
+        if os.name == "java":
+            for node in ast.walk(serialized):
+                if isinstance(node, ast.Str):
+                    if isinstance(node.s, str) and any(c for c in node.s if c 
> '\x7f'):
+                        # In this case there is risk of incorrectly parsed 
unicode data. Play safe and crash.
+                        raise ValueError("cannot properly parse unicode string 
with ast in Jython, see bug http://bugs.jython.org/issue2008";
+                                         " - use python 2.x server or convert 
strings to ascii yourself first")
+                    node.s = node.s.decode("unicode-escape")
     try:
-        gc.disable()
+        if os.name != "java" and sys.platform != "cli":
+            gc.disable()
         return ast.literal_eval(serialized)
     finally:
         gc.enable()
@@ -108,12 +164,12 @@
     _special_classes_registry[KeysView] = _ser_DictView
     _special_classes_registry[ValuesView] = _ser_DictView
     _special_classes_registry[ItemsView] = _ser_DictView
-    _special_classes_registry[collections.OrderedDict] = _ser_OrderedDict
-
-    def _ser_Enum(obj, serializer, outputstream, indentlevel):
-        serializer._serialize(obj.value, outputstream, indentlevel)
-
-    _special_classes_registry[enum.Enum] = _ser_Enum
+    if sys.version_info >= (2, 7):
+        _special_classes_registry[collections.OrderedDict] = _ser_OrderedDict
+    if enum is not None:
+        def _ser_Enum(obj, serializer, outputstream, indentlevel):
+            serializer._serialize(obj.value, outputstream, indentlevel)
+        _special_classes_registry[enum.Enum] = _ser_Enum
 
 
 _reset_special_classes_registry()
@@ -143,7 +199,12 @@
         self.data = data
 
     def __getstate__(self):
-        b64 = base64.b64encode(self.data)
+        if sys.platform == "cli":
+            b64 = base64.b64encode(str(self.data))  # weird IronPython bug?
+        elif (os.name == "java" or sys.version_info < (2, 7)) and 
type(self.data) is bytearray:
+            b64 = base64.b64encode(bytes(self.data))  # Jython bug 
http://bugs.jython.org/issue2011
+        else:
+            b64 = base64.b64encode(self.data)
         return {
             "data": b64 if type(b64) is str else b64.decode("ascii"),
             "encoding": "base64"
@@ -161,27 +222,45 @@
     def from_memoryview(data):
         return BytesWrapper(data.tobytes())
 
+    @staticmethod
+    def from_buffer(data):
+        return BytesWrapper(data)
+
 
-_repr_types = {str, int, bool, type(None)}
+_repr_types = set([
+    str,
+    int,
+    bool,
+    type(None)
+])
 
 _translate_types = {
     bytes: BytesWrapper.from_bytes,
     bytearray: BytesWrapper.from_bytearray,
     collections.deque: list,
-    collections.UserDict: dict,
-    collections.UserList: list,
-    collections.UserString: str
 }
 
+if sys.version_info >= (3, 0):
+    _translate_types.update({
+        collections.UserDict: dict,
+        collections.UserList: list,
+        collections.UserString: str
+    })
+
 _bytes_types = [bytes, bytearray, memoryview]
 
 # do some dynamic changes to the types configuration if needed
 if bytes is str:
     del _translate_types[bytes]
+if hasattr(types, "BufferType"):
+    _translate_types[types.BufferType] = BytesWrapper.from_buffer
+    _bytes_types.append(buffer)
 try:
     _translate_types[memoryview] = BytesWrapper.from_memoryview
 except NameError:
     pass
+if sys.platform == "cli":
+    _repr_types.remove(str)  # IronPython needs special str treatment, 
otherwise it treats unicode wrong
 _bytes_types = tuple(_bytes_types)
 
 
@@ -211,14 +290,16 @@
     """
     dispatch = {}
 
-    def __init__(self, indent=False, module_in_classname=False):
+    def __init__(self, indent=False, set_literals=can_use_set_literals, 
module_in_classname=False):
         """
         Initialize the serializer.
         indent=indent the output over multiple lines (default=false)
+        setLiterals=use set-literals or not (set to False if you need 
compatibility with Python < 3.2).
         Serpent chooses a sensible default for you.
         module_in_classname = include module prefix for class names or only 
use the class name itself
         """
         self.indent = indent
+        self.set_literals = set_literals
         self.module_in_classname = module_in_classname
         self.serialized_obj_ids = set()
         self.special_classes_registry_copy = None
@@ -227,10 +308,17 @@
     def serialize(self, obj):
         """Serialize the object tree to bytes."""
         self.special_classes_registry_copy = _special_classes_registry.copy()  
 # make it thread safe
-        header = "# serpent utf-8 python3.2\n"
+        header = "# serpent utf-8 "
+        if self.set_literals:
+            header += "python3.2\n"   # set-literals require python 3.2+ to 
deserialize (ast.literal_eval limitation)
+        else:
+            header += "python2.6\n"   # don't change this, otherwise we can't 
read older serpent strings
         out = [header]
+        if os.name == "java" and type(obj) is buffer:
+            obj = bytearray(obj)
         try:
-            gc.disable()
+            if os.name != "java" and sys.platform != "cli":
+                gc.disable()
             self.serialized_obj_ids = set()
             self._serialize(obj, out, 0)
         finally:
@@ -239,7 +327,7 @@
         del self.serialized_obj_ids
         return "".join(out).encode("utf-8")
 
-    _shortcut_dispatch_types = {float, complex, tuple, list, dict, set, 
frozenset}
+    _shortcut_dispatch_types = frozenset([float, complex, tuple, list, dict, 
set, frozenset])
 
     def _serialize(self, obj, out, level):
         if level > self.maximum_level:
@@ -275,6 +363,11 @@
                 func = Serializer.ser_default_class
         func(self, obj, out, level)
 
+    def ser_builtins_str(self, str_obj, out, level):
+        # special case str, for IronPython where str==unicode and repr() 
yields undesired result
+        self.ser_builtins_unicode(str_obj, out, level)
+    dispatch[str] = ser_builtins_str
+
     def ser_builtins_float(self, float_obj, out, level):
         if math.isnan(float_obj):
             # there's no literal expression for a float NaN...
@@ -298,6 +391,21 @@
         out.append("j)")
     dispatch[complex] = ser_builtins_complex
 
+    if sys.version_info < (3, 0):
+        # this method is used for python 2.x unicode (python 3.x doesn't use 
this)
+        def ser_builtins_unicode(self, unicode_obj, out, level):
+            z = repr(unicode_obj)
+            if z[0] == 'u':
+                z = z[1:]    # get rid of the unicode 'u' prefix
+            out.append(z)
+        dispatch[unicode] = ser_builtins_unicode
+
+    if sys.version_info < (3, 0):
+        def ser_builtins_long(self, long_obj, out, level):
+            # used with python 2.x
+            out.append(str(long_obj))
+        dispatch[long] = ser_builtins_long
+
     def ser_builtins_tuple(self, tuple_obj, out, level):
         append = out.append
         serialize = self._serialize
@@ -352,7 +460,9 @@
 
     def _check_hashable_type(self, t):
         if t not in (bool, bytes, str, tuple) and not issubclass(t, 
numbers.Number):
-            if issubclass(t, enum.Enum):
+            if enum is not None and issubclass(t, enum.Enum):
+                return
+            elif sys.version_info < (3, 0) and t is unicode:
                 return
             raise TypeError("one of the keys in a dict or set is not of a 
primitive hashable type: " +
                             str(t) + ". Use simple types as keys or use a list 
or tuple as container.")
@@ -396,6 +506,11 @@
     dispatch[dict] = ser_builtins_dict
 
     def ser_builtins_set(self, set_obj, out, level):
+        if not self.set_literals:
+            if self.indent:
+                set_obj = sorted(set_obj)
+            self._serialize(tuple(set_obj), out, level)     # use a tuple 
instead of a set literal
+            return
         append = out.append
         serialize = self._serialize
         if self.indent and set_obj:
@@ -443,9 +558,14 @@
         out.append(repr(date_obj.isoformat()))
     dispatch[datetime.date] = ser_datetime_date
 
-    def ser_datetime_timedelta(self, timedelta_obj, out, level):
-        secs = timedelta_obj.total_seconds()
-        out.append(repr(secs))
+    if os.name == "java" or sys.version_info < (2, 7):    # jython bug 
http://bugs.jython.org/issue2010
+        def ser_datetime_timedelta(self, timedelta_obj, out, level):
+            secs = ((timedelta_obj.days * 86400 + timedelta_obj.seconds) * 10 
** 6 + timedelta_obj.microseconds) / 10 ** 6
+            out.append(repr(secs))
+    else:
+        def ser_datetime_timedelta(self, timedelta_obj, out, level):
+            secs = timedelta_obj.total_seconds()
+            out.append(repr(secs))
     dispatch[datetime.timedelta] = ser_datetime_timedelta
 
     def ser_datetime_time(self, time_obj, out, level):
@@ -467,7 +587,9 @@
     dispatch[BaseException] = ser_exception_class
 
     def ser_array_array(self, array_obj, out, level):
-        if array_obj.typecode == 'u':
+        if array_obj.typecode == 'c':
+            self._serialize(array_obj.tostring(), out, level)
+        elif array_obj.typecode == 'u':
             self._serialize(array_obj.tounicode(), out, level)
         else:
             self._serialize(array_obj.tolist(), out, level)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/setup.cfg new/serpent-1.28/setup.cfg
--- old/serpent-1.30.2/setup.cfg        2020-02-01 10:36:38.472208500 +0100
+++ new/serpent-1.28/setup.cfg  2019-03-10 17:33:49.000000000 +0100
@@ -1,5 +1,5 @@
 [wheel]
-universal = 0
+universal = 1
 
 [bdist_rpm]
 doc_files = LICENSE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/setup.py new/serpent-1.28/setup.py
--- old/serpent-1.30.2/setup.py 2020-02-01 10:25:57.000000000 +0100
+++ new/serpent-1.28/setup.py   2019-03-08 23:02:46.000000000 +0100
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 # Serpent: ast.literal_eval() compatible object tree serialization.
+# Copyright 2013, Irmen de Jong ([email protected])
 # Software license: "MIT software license". See 
http://opensource.org/licenses/MIT
 
 try:
@@ -11,15 +12,8 @@
 
 import unittest
 import re
-import sys
-
 serpent_version = re.search(r'^__version__\s*=\s*"(.+)"', open("serpent.py", 
"rt").read(), re.MULTILINE).groups()[0]
 
-if sys.version_info < (3, 2):
-    raise RuntimeError("This version of serpent ({}) doesn't support this 
obsolete Python version {}.{}. "
-                       "Either upgrade to a recent Python version or downgrade 
serpent to a version before 1.30"
-                       .format(serpent_version, sys.version_info.major, 
sys.version_info.minor))
-
 
 def serpent_test_suite():
     testloader = unittest.TestLoader()
@@ -31,11 +25,9 @@
     name='serpent',
     version=serpent_version,
     py_modules=["serpent"],
-    python_requires='>=3.2',
     license='MIT',
     author='Irmen de Jong',
     author_email='[email protected]',
-    url='https://github.com/irmen/Serpent',
     description='Serialization based on ast.literal_eval',
     long_description="""
 Serpent is a simple serialization library based on ast.literal_eval.
@@ -52,7 +44,7 @@
 
 **API**
 
-- ``ser_bytes = serpent.dumps(obj, indent=False, module_in_classname=False):`` 
     # serialize obj tree to bytes
+- ``ser_bytes = serpent.dumps(obj, indent=False, set_literals=True, 
module_in_classname=False):``      # serialize obj tree to bytes
 - ``obj = serpent.loads(ser_bytes)``     # deserialize bytes back into object 
tree
 - You can use ``ast.literal_eval`` yourself to deserialize, but 
``serpent.deserialize``
   works around a few corner cases. See source for details.
@@ -72,7 +64,7 @@
 Serpent allows comments in the serialized data (because it is just Python 
source code).
 Serpent can't serialize object graphs (when an object refers to itself); it 
will then crash with a ValueError pointing out the problem.
 
-Works with Python 3.5+
+Works with Python 2.7+ (including 3.x), IronPython 2.7+, Jython 2.7+.
 
 **FAQ**
 
@@ -94,7 +86,9 @@
 
 .. code:: python
 
+ # This demo script is written for Python 3.2+
  # -*- coding: utf-8 -*-
+ from __future__ import print_function
  import ast
  import uuid
  import datetime
@@ -136,7 +130,7 @@
  assert data2==data
 
 
-When you run this it prints:
+When you run this (with python 3.2+) it prints:
 
 .. code:: python
 
@@ -181,11 +175,13 @@
         "Natural Language :: English",
         "Operating System :: OS Independent",
         "Programming Language :: Python",
+        "Programming Language :: Python :: 2.7",
+        "Programming Language :: Python :: 3.4",
         "Programming Language :: Python :: 3.5",
         "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
-        "Programming Language :: Python :: 3.8",
         "Topic :: Software Development"
     ],
+    tests_require=['enum34; python_version < "3.4"'],
     test_suite="setup.serpent_test_suite"
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/tests/example.py 
new/serpent-1.28/tests/example.py
--- old/serpent-1.30.2/tests/example.py 2020-01-21 23:56:10.000000000 +0100
+++ new/serpent-1.28/tests/example.py   2018-08-16 09:49:54.000000000 +0200
@@ -1,37 +1,38 @@
-import datetime
-import serpent
-
-
-class CustomClass(object):
-    def __init__(self, name, age):
-        self.name = name
-        self.age = age
-
-
-def example():
-    data = {
-        "tuple": (1, 2, 3),
-        "date": datetime.datetime.now(),
-        "set": {'a', 'b', 'c'},
-        "class": CustomClass("Sally", 26)
-    }
-
-    # serialize the object
-    ser = serpent.dumps(data, indent=True)
-    # print it to the screen, but usually you'd save the bytes to a file or 
transfer them over a network connection
-    print("Serialized data:")
-    print(ser.decode("UTF-8"))
-
-    # deserialize the bytes and print the objects
-    obj = serpent.loads(ser)
-    print("Deserialized data:")
-    print("tuple:", obj["tuple"])
-    print("date:", obj["date"])
-    print("set:", obj["set"])
-    clazz = obj["class"]
-    print("class attributes: type={0} name={1} age={2}".format(
-        clazz["__class__"], clazz["name"], clazz["age"]))
-
-
-if __name__ == "__main__":
-    example()
+from __future__ import print_function
+import datetime
+import serpent
+
+
+class CustomClass(object):
+    def __init__(self, name, age):
+        self.name = name
+        self.age = age
+
+
+def example():
+    data = {
+        "tuple": (1, 2, 3),
+        "date": datetime.datetime.now(),
+        "set": set(['a', 'b', 'c']),
+        "class": CustomClass("Sally", 26)
+    }
+
+    # serialize the object
+    ser = serpent.dumps(data, indent=True)
+    # print it to the screen, but usually you'd save the bytes to a file or 
transfer them over a network connection
+    print("Serialized data:")
+    print(ser.decode("UTF-8"))
+
+    # deserialize the bytes and print the objects
+    obj = serpent.loads(ser)
+    print("Deserialized data:")
+    print("tuple:", obj["tuple"])
+    print("date:", obj["date"])
+    print("set:", obj["set"])
+    clazz = obj["class"]
+    print("class attributes: type={0} name={1} age={2}".format(
+        clazz["__class__"], clazz["name"], clazz["age"]))
+
+
+if __name__ == "__main__":
+    example()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/tests/performance.py 
new/serpent-1.28/tests/performance.py
--- old/serpent-1.30.2/tests/performance.py     2020-01-21 23:45:44.000000000 
+0100
+++ new/serpent-1.28/tests/performance.py       2018-08-16 09:49:54.000000000 
+0200
@@ -3,6 +3,7 @@
 Compares results based on size of the output, and time taken to (de)serialize.
 """
 
+from __future__ import print_function
 from timeit import default_timer as perf_timer
 import sys
 import datetime
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/tests/test_serpent.py 
new/serpent-1.28/tests/test_serpent.py
--- old/serpent-1.30.2/tests/test_serpent.py    2020-01-22 00:06:22.000000000 
+0100
+++ new/serpent-1.28/tests/test_serpent.py      2019-03-08 22:58:27.000000000 
+0100
@@ -1,7 +1,11 @@
 """
 Serpent: ast.literal_eval() compatible object tree serialization.
+
+Copyright 2013, Irmen de Jong ([email protected])
 Software license: "MIT software license". See 
http://opensource.org/licenses/MIT
 """
+from __future__ import print_function, division
+import __future__
 import sys
 import ast
 import timeit
@@ -15,16 +19,33 @@
 import traceback
 import threading
 import time
+import types
 import collections
 import enum
 import attr
-import unittest
-from collections.abc import KeysView, ValuesView, ItemsView
+if sys.version_info >= (3, 4):
+    from collections.abc import KeysView, ValuesView, ItemsView
+else:
+    from collections import KeysView, ValuesView, ItemsView
+
+if sys.version_info < (2, 7):
+    import unittest2 as unittest
+else:
+    import unittest
+
 import serpent
 
 
+if sys.version_info >= (3, 0):
+    unicode = str
+    unichr = chr
+
+
 def strip_header(ser):
-    _, _, data = ser.partition(b"\n")
+    if sys.platform == "cli":
+        _, _, data = ser.partition("\n")
+    else:
+        _, _, data = ser.partition(b"\n")
     return data
 
 
@@ -33,12 +54,13 @@
         data = serpent.loads(b"555")
         self.assertEqual(555, data)
 
-    def test_deserialize_chr(self):
+    def test_deserialize_unichr(self):
         unicodestring = u"euro\u20ac"
         encoded = repr(unicodestring).encode("utf-8")
         data = serpent.loads(encoded)
         self.assertEqual(unicodestring, data)
 
+    @unittest.skipIf(sys.version_info < (3, 0), "Python 2.x ast can't parse 
complex")
     def test_weird_complex(self):
         c1 = complex(float('inf'), 4)
         ser = serpent.dumps(c1)
@@ -55,9 +77,10 @@
         v = serpent.loads(b"{'a':1, 'b':2, 'c':3,}")
         self.assertEqual({'a': 1, 'b': 2, 'c': 3}, v)
 
+    @unittest.skipIf(sys.version_info < (3, 2), "needs python 3.3+ to parse 
set literals")
     def test_trailing_comma_set(self):
         v = serpent.loads(b"{1,2,3,}")
-        self.assertEqual({1, 2, 3}, v)
+        self.assertEqual(set([1, 2, 3]), v)
 
     def test_unicode_escapes(self):
         v = serpent.loads(b"'\\u20ac'")
@@ -72,6 +95,9 @@
         self.assertEqual("text", serpent.loads(bytes_input))
         self.assertEqual("text", serpent.loads(bytearray_input))
         self.assertEqual("text", serpent.loads(memview_input))
+        if sys.version_info < (3, 0):
+            buffer_input = buffer(bytes_input)
+            self.assertEqual("text", serpent.loads(buffer_input))
 
 
 class TestBasics(unittest.TestCase):
@@ -83,19 +109,35 @@
         self.assertEqual(data, result, "must understand python 2.x repr form 
of unicode string")
         py3repr = b"# serpent utf-8 python3.2\n'hello\xe2\x82\xac'"
         try:
-            result = serpent.loads(py3repr)
-            self.assertEqual(data, result, "must understand python 3.x repr 
form of unicode string")
-        except ValueError:
-            self.fail("must parse it correctly")
+            result = serpent.loads(py3repr)   # jython fails this test.
+            if os.name != "java":
+                self.assertEqual(data, result, "must understand python 3.x 
repr form of unicode string")
+        except ValueError as x:
+            if os.name == "java":
+                self.assertIn("issue2008", str(x))
+            else:
+                self.fail("non-jython must parse it correctly")
 
     def test_header(self):
-        ser = serpent.dumps(None)
-        header, _, rest = ser.partition(b"\n")
+        ser = serpent.dumps(None, set_literals=True)
+        if sys.platform == "cli":
+            header, _, rest = ser.partition("\n")
+        else:
+            self.assertTrue(type(ser) is bytes)
+            header, _, rest = ser.partition(b"\n")
         hdr = "# serpent utf-8 python3.2".encode("utf-8")
         self.assertEqual(hdr, header)
+        ser = serpent.dumps(None, set_literals=False)
+        if sys.platform == "cli":
+            header, _, rest = ser.partition("\n")
+        else:
+            self.assertTrue(type(ser) is bytes)
+            header, _, rest = ser.partition(b"\n")
+        hdr = "# serpent utf-8 python2.6".encode("utf-8")    # don't change 
the 2.6 here even though we don't support python 2.6 any longer
+        self.assertEqual(hdr, header)
 
     def test_comments(self):
-        ser = b"""# serpent utf-8 python3.2
+        ser = b"""# serpent utf-8 python2.7
 [ 1, 2,
    # some comments here
    3, 4]    # more here
@@ -121,32 +163,32 @@
         ser = serpent.dumps(obj)
         data = strip_header(ser)
         self.assertEqual(36, len(data))
-        obj = {3, 4, 2, 1, 6, 5}
+        obj = set([3, 4, 2, 1, 6, 5])
         ser = serpent.dumps(obj)
         data = strip_header(ser)
         self.assertEqual(13, len(data))
-        ser = serpent.dumps(obj, indent=True)
+        ser = serpent.dumps(obj, indent=True, set_literals=True)
         data = strip_header(ser)
         self.assertEqual(b"{\n  1,\n  2,\n  3,\n  4,\n  5,\n  6\n}", data)     
 # sorted
 
-        obj = {3, "something"}
-        ser = serpent.dumps(obj, indent=False)
+        obj = set([3, "something"])
+        ser = serpent.dumps(obj, indent=False, set_literals=True)
         data = strip_header(ser)
         self.assertTrue(data == b"{3,'something'}" or data == 
b"{'something',3}")
-        ser = serpent.dumps(obj, indent=True)
+        ser = serpent.dumps(obj, indent=True, set_literals=True)
         data = strip_header(ser)
         self.assertTrue(data == b"{\n  3,\n  'something'\n}" or data == b"{\n  
'something',\n  3\n}")
 
         obj = {3: "three", "something": 99}
-        ser = serpent.dumps(obj, indent=False)
+        ser = serpent.dumps(obj, indent=False, set_literals=True)
         data = strip_header(ser)
         self.assertTrue(data == b"{'something':99,3:'three'}" or data == 
b"{3:'three','something':99}")
-        ser = serpent.dumps(obj, indent=True)
+        ser = serpent.dumps(obj, indent=True, set_literals=True)
         data = strip_header(ser)
         self.assertTrue(data == b"{\n  'something': 99,\n  3: 'three'\n}" or 
data == b"{\n  3: 'three',\n  'something': 99\n}")
 
         obj = {3: "three", 4: "four", 5: "five", 2: "two", 1: "one"}
-        ser = serpent.dumps(obj, indent=True)
+        ser = serpent.dumps(obj, indent=True, set_literals=True)
         data = strip_header(ser)
         self.assertEqual(b"{\n  1: 'one',\n  2: 'two',\n  3: 'three',\n  4: 
'four',\n  5: 'five'\n}", data)   # sorted
 
@@ -197,8 +239,8 @@
         data = serpent.loads(ser)
         self.assertEqual(" ", data)
 
-    def test_nullbytesstr(self):
-        line = chr(0) + "null"
+    def test_nullbytesunicode(self):
+        line = unichr(0) + "null"
         ser = serpent.dumps(line)
         data = strip_header(ser)
         self.assertEqual(b"'\\x00null'", data, "must escape 0-byte")
@@ -221,9 +263,10 @@
         serpent.loads(bytearray(b"'contains no nullbyte'"))
         serpent.loads(memoryview(b"'contains no nullbyte'"))
 
+    @unittest.skipIf(os.name == "java", "jython can't parse unicode U's")
     def test_unicode_U(self):
-        u = "euro" + chr(0x20ac)+"\U00022001"
-        self.assertTrue(type(u) is str)
+        u = "euro" + unichr(0x20ac)+"\U00022001"
+        self.assertTrue(type(u) is unicode)
         ser = serpent.dumps(u)
         data = serpent.loads(ser)
         self.assertEqual(u, data)
@@ -232,22 +275,34 @@
         # this checks for all 0x0000-0xffff chars that they will be serialized
         # into a proper repr form and when processed back by ast.literal_parse 
directly
         # will get turned back into the chars 0x0000-0xffff again
-        highest_char = 0xffff
-        all_chars = u"".join(chr(c) for c in range(highest_char+1))
+        # For Jython we take the range upto 0x4100 because after that it starts
+        # complaining about surrogates or "mark invalid" (an utf-8 parsing bug 
it seems)
+        highest_char = 0x4100 if os.name == "java" else 0xffff
+        all_chars = u"".join(unichr(c) for c in range(highest_char+1))
         ser = serpent.dumps(all_chars)
         self.assertGreater(len(ser), len(all_chars))
         ser = ser.decode("utf-8")
+        if sys.version_info < (3, 0):
+            ser = compile(ser, "<serpent>", mode="eval", 
flags=ast.PyCF_ONLY_AST | __future__.unicode_literals.compiler_flag)
+            if os.name == "java":
+                # The ast module in Jython will not have parsed this correctly 
into unicode literals.
+                # So we have to patch up the ast tree ourselves and decode Str 
nodes to unicode manually.
+                # (this is the same what Serpent does internally so we 
replicate it here)
+                # See http://bugs.jython.org/issue2008
+                for node in ast.walk(ser):
+                    if isinstance(node, ast.Str):
+                        node.s = node.s.decode("unicode-escape")
         data = ast.literal_eval(ser)
         self.assertEqual(highest_char+1, len(data))
         for i, c in enumerate(data):
-            if chr(i) != c:
+            if unichr(i) != c:
                 self.fail("char different for "+str(i))
 
     def test_unicode_quotes(self):
-        ser = serpent.dumps(str("quotes'\""))
+        ser = serpent.dumps(unicode("quotes'\""))
         data = strip_header(ser)
         self.assertEqual(b"'quotes\\'\"'", data)
-        ser = serpent.dumps(str("quotes2'"))
+        ser = serpent.dumps(unicode("quotes2'"))
         data = strip_header(ser)
         self.assertEqual(b"\"quotes2'\"", data)
 
@@ -255,22 +310,44 @@
         u = u"\x00\x01\x80\x81\xfe\xffabcdef\u20ac"
         utf_8_correct = repr(u).encode("utf-8")
         if utf_8_correct.startswith(b"u"):
-            utf_8_correct = utf_8_correct[1:]
+            utf_8_correct=utf_8_correct[1:]
         ser = serpent.dumps(u)
         d = strip_header(ser)
         self.assertEqual(utf_8_correct, d)
 
+    @unittest.skipIf(sys.version_info >= (3, 0), "py2 escaping tested")
+    def test_unicode_with_escapes_py2(self):
+        ser = serpent.dumps(unicode("\n"))
+        d = strip_header(ser)
+        self.assertEqual(b"'\\n'", d)
+        ser = serpent.dumps(unicode("\a"))
+        d = strip_header(ser)
+        self.assertEqual(b"'\\x07'", d)
+
+    @unittest.skipIf(sys.version_info >= (3, 0), "py2 escaping tested")
+    def test_unicode_with_escapes_unichrs(self):
+        ser = serpent.dumps("\a"+unichr(0x20ac))
+        d = strip_header(ser)
+        self.assertEqual(b"'\\x07\\u20ac'", d)
+        line = "'euro" + unichr(0x20ac) + 
"\nlastline\ttab\\@slash\a\b\f\n\r\t\v'"
+        ser = serpent.dumps(line)
+        d = strip_header(ser)
+        
self.assertEqual(b"\"'euro\\u20ac\\nlastline\\ttab\\\\@slash\\x07\\x08\\x0c\\n\\r\\t\\x0b'\"",
 d)
+        data = serpent.loads(ser)
+        self.assertEqual(line, data)
+
+    @unittest.skipIf(sys.version_info < (3, 0), "py3 escaping tested")
     def test_unicode_with_escapes_py3(self):
-        ser = serpent.dumps(str("\n"))
+        ser = serpent.dumps(unicode("\n"))
         d = strip_header(ser)
         self.assertEqual(b"'\\n'", d)
-        ser = serpent.dumps(str("\a"))
+        ser = serpent.dumps(unicode("\a"))
         d = strip_header(ser)
         self.assertEqual(b"'\\x07'", d)
-        ser = serpent.dumps("\a"+chr(0x20ac))
+        ser = serpent.dumps("\a"+unichr(0x20ac))
         d = strip_header(ser)
         self.assertEqual(b"'\\x07\xe2\x82\xac'", d)
-        line = "'euro" + chr(0x20ac) + "\nlastline\ttab\\@slash\a\b\f\n\r\t\v'"
+        line = "'euro" + unichr(0x20ac) + 
"\nlastline\ttab\\@slash\a\b\f\n\r\t\v'"
         ser = serpent.dumps(line)
         d = strip_header(ser)
         
self.assertEqual(b"\"'euro\xe2\x82\xac\\nlastline\\ttab\\\\@slash\\x07\\x08\\x0c\\n\\r\\t\\x0b'\"",
 d)
@@ -322,23 +399,31 @@
         ser = serpent.dumps(mydict)
         data = strip_header(ser)
         self.assertEqual(69, len(data))
-        self.assertEqual(ord("{"), data[0])
-        self.assertEqual(ord("}"), data[-1])
+        if sys.version_info < (3, 0):
+            self.assertEqual(b"{", data[0])
+            self.assertEqual(b"}", data[-1])
+        else:
+            self.assertEqual(ord("{"), data[0])
+            self.assertEqual(ord("}"), data[-1])
         ser = serpent.dumps(mydict, indent=True)
         data = strip_header(ser)
         self.assertEqual(86, len(data))
-        self.assertEqual(ord("{"), data[0])
-        self.assertEqual(ord("}"), data[-1])
+        if sys.version_info < (3, 0):
+            self.assertEqual(b"{", data[0])
+            self.assertEqual(b"}", data[-1])
+        else:
+            self.assertEqual(ord("{"), data[0])
+            self.assertEqual(ord("}"), data[-1])
 
-    def test_dict_str(self):
-        data = {"key": str("value")}
+    def test_dict_unicode(self):
+        data = {"key": unicode("value")}
         ser = serpent.dumps(data)
         data2 = serpent.loads(ser)
-        self.assertEqual(str("value"), data2["key"])
-        data = {str("key"): 123}
+        self.assertEqual(unicode("value"), data2["key"])
+        data = {unicode("key"): 123}
         ser = serpent.dumps(data)
         data2 = serpent.loads(ser)
-        self.assertEqual(123, data2[str("key")])
+        self.assertEqual(123, data2[unicode("key")])
 
     def test_dict_iters(self):
         data = {"john": 22, "sophie": 34, "bob": 26}
@@ -408,31 +493,43 @@
         self.assertEqual(b"()", data)
 
         # test set-literals
-        myset = {42, "Sally"}
-        ser = serpent.dumps(myset)
+        myset = set([42, "Sally"])
+        ser = serpent.dumps(myset, set_literals=True)
         data = strip_header(ser)
         self.assertTrue(data == b"{42,'Sally'}" or data == b"{'Sally',42}")
-        ser = serpent.dumps(myset, indent=True)
+        ser = serpent.dumps(myset, indent=True, set_literals=True)
         data = strip_header(ser)
         self.assertTrue(data == b"{\n  42,\n  'Sally'\n}" or data == b"{\n  
'Sally',\n  42\n}")
+
+        # test no set-literals
+        ser = serpent.dumps(myset, set_literals=False)
+        data = strip_header(ser)
+        self.assertTrue(data == b"(42,'Sally')" or data == b"('Sally',42)")    
# must output a tuple instead of a set-literal
+
         # unicode elements
-        data = {str("text1"), str("text2")}
+        data = set([unicode("text1"), unicode("text2")])
         ser = serpent.dumps(data)
         data2 = serpent.loads(ser)
         self.assertEqual(2, len(data2))
-        self.assertIn(str("text1"), data2)
-        self.assertIn(str("text2"), data2)
+        self.assertIn(unicode("text1"), data2)
+        self.assertIn(unicode("text2"), data2)
 
     def test_bytes(self):
-        ser = serpent.dumps(bytes(b"abcdef"))
-        data = serpent.loads(ser)
-        self.assertEqual({'encoding': 'base64', 'data': 'YWJjZGVm'}, data)
+        if sys.version_info >= (3, 0):
+            ser = serpent.dumps(bytes(b"abcdef"))
+            data = serpent.loads(ser)
+            self.assertEqual({'encoding': 'base64', 'data': 'YWJjZGVm'}, data)
         ser = serpent.dumps(bytearray(b"abcdef"))
         data = serpent.loads(ser)
         self.assertEqual({'encoding': 'base64', 'data': 'YWJjZGVm'}, data)
-        ser = serpent.dumps(memoryview(b"abcdef"))
-        data = serpent.loads(ser)
-        self.assertEqual({'encoding': 'base64', 'data': 'YWJjZGVm'}, data)
+        if sys.version_info >= (2, 7):
+            ser = serpent.dumps(memoryview(b"abcdef"))
+            data = serpent.loads(ser)
+            self.assertEqual({'encoding': 'base64', 'data': 'YWJjZGVm'}, data)
+        if sys.version_info < (3, 0):
+            ser = serpent.dumps(buffer(b"abcdef"))
+            data = serpent.loads(ser)
+            self.assertEqual({'encoding': 'base64', 'data': 'YWJjZGVm'}, data)
 
     def test_exception(self):
         x = ZeroDivisionError("wrong")
@@ -467,8 +564,12 @@
         x = ZeroDivisionError("wrong")
         ser = serpent.dumps(x, module_in_classname=True)
         data = serpent.loads(ser)
+        if sys.version_info < (3, 0):
+            expected_classname = "exceptions.ZeroDivisionError"
+        else:
+            expected_classname = "builtins.ZeroDivisionError"
         self.assertEqual({
-            '__class__': "builtins.ZeroDivisionError",
+            '__class__': expected_classname,
             '__exception__': True,
             'args': ('wrong',),
             'attributes': {}
@@ -525,6 +626,7 @@
             serpent.dumps({1: 1, 2: 1, 3: 1, pp: 1})    # can only serialize 
simple types as dict keys (hashable)
         self.assertTrue("hashable type" in str(x.exception))
 
+    @unittest.skipIf(not serpent.can_use_set_literals, reason="no problem if 
serpent doesn't serializes set literals")
     def test_class_hashable_set_element_check(self):
         import pprint
         pp = pprint.PrettyPrinter(stream="dummy", width=42)
@@ -542,21 +644,28 @@
             BLUE = 3
         data = serpent.dumps({"abc", Color.RED, Color.GREEN, Color.BLUE})
         orig = serpent.loads(data)
-        self.assertEqual({"abc", 1, 2, 3}, orig)
+        if sys.version_info < (3, 4):
+            self.assertEqual([1, 2, 3, u"abc"], sorted(orig))
+        else:
+            self.assertEqual({"abc", 1, 2, 3}, orig)
         data = serpent.dumps({"abc": 1, Color.RED: 1, Color.GREEN: 1, 
Color.BLUE: 1})
         orig = serpent.loads(data)
-        self.assertEqual({"abc": 1, 1: 1, 2: 1, 3: 1}, orig)
+        if sys.version_info < (3, 4):
+            self.assertEqual({u"abc": 1, 1: 1, 2: 1, 3: 1}, orig)
+        else:
+            self.assertEqual({"abc": 1, 1: 1, 2: 1, 3: 1}, orig)
 
     def test_array(self):
-        ser = serpent.dumps(array.array('u', str("unicode")))
+        ser = serpent.dumps(array.array('u', unicode("unicode")))
         data = strip_header(ser)
         self.assertEqual(b"'unicode'", data)
         ser = serpent.dumps(array.array('i', [44, 45, 46]))
         data = strip_header(ser)
         self.assertEqual(b"[44,45,46]", data)
-        ser = serpent.dumps(array.array('u', "normal"))
-        data = strip_header(ser)
-        self.assertEqual(b"'normal'", data)
+        if sys.version_info < (3, 0):
+            ser = serpent.dumps(array.array('c', "normal"))
+            data = strip_header(ser)
+            self.assertEqual(b"'normal'", data)
 
     def test_time(self):
         ser = serpent.dumps(datetime.datetime(2013, 1, 20, 23, 59, 45, 999888))
@@ -598,7 +707,7 @@
         serpent.loads(ser)
         tmpfn = tempfile.mktemp()
         with open(tmpfn, "wb") as outf:
-            serpent.dump([1, 2, 3], outf, indent=True)
+            serpent.dump([1, 2, 3], outf, indent=True, set_literals=True)
         with open(tmpfn, "rb") as inf:
             data = serpent.load(inf)
             self.assertEqual([1, 2, 3], data)
@@ -610,7 +719,7 @@
         ser = strip_header(serpent.dumps(values))
         
self.assertEqual(b"[1e30000,-1e30000,{'__class__':'float','value':'nan'},(1e30000+4.0j)]",
 ser)
         values2 = serpent.loads(ser)
-        self.assertEqual([float('inf'), float('-inf'), {'__class__': 'float', 
'value': 'nan'}, (float('inf')+4j)], values2)
+        self.assertEqual([float('inf'), float('-inf'), 
{'__class__':'float','value':'nan'}, (float('inf')+4j)], values2)
         values2 = serpent.loads(b"[1e30000,-1e30000]")
         self.assertEqual([float('inf'), float('-inf')], values2)
 
@@ -652,10 +761,16 @@
         self.assertIs(obj, serpent.tobytes(obj))
         obj = bytearray(b"test")
         self.assertIs(obj, serpent.tobytes(obj))
+        if hasattr(types, "BufferType"):
+            obj = buffer(b"test")
+            self.assertIs(obj, serpent.tobytes(obj))
         ser = {'data': 'dGVzdA==', 'encoding': 'base64'}
         out = serpent.tobytes(ser)
         self.assertEqual(b"test", out)
-        self.assertIsInstance(out, bytes)
+        if sys.platform == 'cli':
+            self.assertIsInstance(out, str)  # ironpython base64 decodes into 
str type....
+        else:
+            self.assertIsInstance(out, bytes)
         with self.assertRaises(TypeError):
             serpent.tobytes({'@@@data': 'dGVzdA==', 'encoding': 'base64'})
         with self.assertRaises(TypeError):
@@ -673,12 +788,12 @@
     def setUp(self):
         self.data = {
             "str": "hello",
-            "unicode": chr(0x20ac),  # euro-character
+            "unicode": unichr(0x20ac),  # euro-character
             "numbers": [123456789012345678901234567890, 999.1234, 
decimal.Decimal("1.99999999999999999991")],
             "bytes": bytearray(100),
             "list": [1, 2, 3, 4, 5, 6, 7, 8, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 
7.7, 8.8, 9.9],
             "tuple": (1, 2, 3, 4, 5, 6, 7, 8),
-            "set": {1, 2, 3, 4, 5, 6, 7, 8, 9},
+            "set": set([1, 2, 3, 4, 5, 6, 7, 8, 9]),
             "dict": dict((i, str(i) * 4) for i in range(10)),
             "exc": ZeroDivisionError("fault"),
             "dates": [
@@ -753,21 +868,21 @@
   2,
   3
 )""", ser)
-        data = {1}
-        ser = serpent.dumps(data, indent=True).decode("utf-8")
+        data = set([1])
+        ser = serpent.dumps(data, indent=True, 
set_literals=True).decode("utf-8")
         _, _, ser = ser.partition("\n")
         self.assertEqual("""{
   1
 }""", ser)
         data = {"one": 1}
-        ser = serpent.dumps(data, indent=True).decode("utf-8")
+        ser = serpent.dumps(data, indent=True, 
set_literals=True).decode("utf-8")
         _, _, ser = ser.partition("\n")
         self.assertEqual("""{
   'one': 1
 }""", ser)
 
-        data = {"first": [1, 2, ("a", "b")], "second": {1: False}, "third": 
{1, 2}}
-        ser = serpent.dumps(data, indent=True).decode("utf-8")
+        data = {"first": [1, 2, ("a", "b")], "second": {1: False}, "third": 
set([1, 2])}
+        ser = serpent.dumps(data, indent=True, 
set_literals=True).decode("utf-8")
         _, _, ser = ser.partition("\n")
         self.assertEqual("""{
   'first': [
@@ -790,6 +905,8 @@
 
 class TestFiledump(unittest.TestCase):
     def testFile(self):
+        if sys.version_info < (3, 2):
+            self.skipTest("testdatafile contains stuff that is not supported 
by ast.literal_eval on Python < 3.2")
         datafile = "testserpent.utf8.bin"
         if not os.path.exists(datafile):
             mypath = os.path.split(__file__)[0]
@@ -902,7 +1019,8 @@
         self.assertEqual(KeysView, classes.pop(0))
         self.assertEqual(ValuesView, classes.pop(0))
         self.assertEqual(ItemsView, classes.pop(0))
-        self.assertEqual(collections.OrderedDict, classes.pop(0))
+        if sys.version_info >= (2, 7):
+            self.assertEqual(collections.OrderedDict, classes.pop(0))
         self.assertEqual(enum.Enum, classes.pop(0))
         self.assertEqual(BaseClass, classes.pop(0))
         self.assertEqual(SubClass, classes.pop(0))
@@ -1046,6 +1164,7 @@
 
 
 class TestCollections(unittest.TestCase):
+    @unittest.skipIf(sys.version_info < (2, 7), "collections.OrderedDict is 
python 2.7+")
     def testOrderedDict(self):
         o = collections.OrderedDict()
         o['apple'] = 1
@@ -1061,7 +1180,21 @@
         d = serpent.dumps(p)
         p2 = serpent.loads(d)
         self.assertEqual((11, 22), p2)
+        # the checks below are valid if named tuples are not serialized by the 
normal tuple serializer:
+        # if sys.version_info < (2, 7) or sys.platform == "cli":
+        #     # named tuple serialization is unfortunately broken on python 
<2.7 or ironpython; it leaves out the actual values
+        #     self.assertEqual({"__class__": "Point"}, p2)
+        # elif os.name == "java":
+        #     # named tuple serialization is unfortunately broken on jython; 
it forgets about the order
+        #     self.assertEqual({"__class__": "Point", "x": 11, "y": 22}, p2)
+        # elif sys.version_info >= (3, 3) or ((2, 7) <= sys.version_info < (3, 
0)):
+        #     # only these versions got it 100% right!
+        #     self.assertEqual({"__class__": "Point", "items": [('x', 11), 
('y', 22)]}, p2)
+        # else:
+        #     # other versions forget about the order....
+        #     self.assertEqual({"__class__": "Point", "x": 11, "y": 22}, p2)
 
+    @unittest.skipIf(sys.version_info < (2, 7), "collections.Counter is python 
2.7+")
     def testCounter(self):
         c = collections.Counter("even")
         d = serpent.dumps(c)
@@ -1074,6 +1207,7 @@
         obj2 = serpent.loads(d)
         self.assertEqual([1, 2, 3], obj2)
 
+    @unittest.skipIf(sys.version_info < (3, 3), "ChainMap is python 3.3+")
     def testChainMap(self):
         c = collections.ChainMap({"a": 1}, {"b": 2}, {"c": 3})
         d = serpent.dumps(c)
@@ -1088,6 +1222,7 @@
         dd2 = serpent.loads(d)
         self.assertEqual({'a': 1, 'b': 2}, dd2)
 
+    @unittest.skipIf(sys.version_info < (3, 0), "collections.UserDict is 
python 3.0+")
     def testUserDict(self):
         obj = collections.UserDict()
         obj['a'] = 1
@@ -1096,12 +1231,14 @@
         obj2 = serpent.loads(d)
         self.assertEqual({'a': 1, 'b': 2}, obj2)
 
+    @unittest.skipIf(sys.version_info < (3, 0), "collections.UserList is 
python 3.0+")
     def testUserList(self):
         obj = collections.UserList([1, 2, 3])
         d = serpent.dumps(obj)
         obj2 = serpent.loads(d)
         self.assertEqual([1, 2, 3], obj2)
 
+    @unittest.skipIf(sys.version_info < (3, 0), "collections.UserString is 
python 3.0+")
     def testUserString(self):
         obj = collections.UserString("test")
         d = serpent.dumps(obj)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/tests/test_unicode.py 
new/serpent-1.28/tests/test_unicode.py
--- old/serpent-1.30.2/tests/test_unicode.py    2020-01-21 23:45:38.000000000 
+0100
+++ new/serpent-1.28/tests/test_unicode.py      2018-08-16 09:49:54.000000000 
+0200
@@ -1,7 +1,11 @@
+from __future__ import print_function
 import sys
 import serpent
 import platform
 
+if sys.version_info>=(3,0):
+    unichr = chr
+
 teststrings = [
     u"",
     u"abc",
@@ -9,9 +13,9 @@
     u"\x00\x01\x80\x81\xfe\xff\u20ac\u4444\u0240slashu:\\uend.\\u20ac(no 
euro!)\\U00022001bigone"
 ]
 
-large = u"".join(chr(i) for i in range(256))
+large = u"".join(unichr(i) for i in range(256))
 teststrings.append(large)
-large = u"".join(chr(i) for i in range(0x20ac+1))
+large = u"".join(unichr(i) for i in range(0x20ac+1))
 teststrings.append(large)
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/tests/test_unicode_parse.py 
new/serpent-1.28/tests/test_unicode_parse.py
--- old/serpent-1.30.2/tests/test_unicode_parse.py      2020-01-21 
23:46:41.000000000 +0100
+++ new/serpent-1.28/tests/test_unicode_parse.py        2018-08-16 
09:49:54.000000000 +0200
@@ -1,3 +1,4 @@
+from __future__ import print_function
 import os
 import io
 import re
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/serpent-1.30.2/tox.ini new/serpent-1.28/tox.ini
--- old/serpent-1.30.2/tox.ini  2020-01-22 00:00:04.000000000 +0100
+++ new/serpent-1.28/tox.ini    2018-08-16 09:49:54.000000000 +0200
@@ -1,13 +1,18 @@
-[tox]
-envlist=py35,py36,py37,py38,pypy3
-
-[testenv]
-deps=
-     pytz
-     attrs
-changedir={toxinidir}/tests
-commands=python -E -Wall -tt -bb test_serpent.py
-
-[testenv:pypy3]
-commands=python -E -Wall -bb test_serpent.py
-# pypy3 doesn't like the -tt option
+[tox]
+envlist=py27,py34,py35,py36,py37,pypy,pypy3
+
+[testenv]
+deps=
+     pytz
+     enum34; python_version<"3.4"
+     attrs
+changedir={toxinidir}/tests
+commands=python -E -Wall -tt -bb test_serpent.py
+
+[testenv:py26]
+deps=unittest2
+     pytz
+
+[testenv:pypy3]
+commands=python -E -Wall -bb test_serpent.py
+# pypy3 doesn't like the -tt option


Reply via email to