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