Hello community,
here is the log from the commit of package python-cloudpickle for openSUSE:Factory checked in at 2019-09-30 15:56:31
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-cloudpickle (Old)
and /work/SRC/openSUSE:Factory/.python-cloudpickle.new.2352 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-cloudpickle"
Mon Sep 30 15:56:31 2019 rev:8 rq:733396 version:1.2.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-cloudpickle/python-cloudpickle.changes 2019-07-31 14:15:18.638794685 +0200
+++ /work/SRC/openSUSE:Factory/.python-cloudpickle.new.2352/python-cloudpickle.changes 2019-09-30 15:56:32.597672606 +0200
@@ -1,0 +2,8 @@
+Thu Sep 26 10:40:19 UTC 2019 - Tomáš Chvátal
+
+- Update to 1.2.2:
+ * Revert the change introduced in (issue #276) attempting to pickle functions annotations for Python 3.4 to 3.6. It is not possible to pickle complex typing constructs for those versions (see issue #193)
+ * Fix a bug affecting bound classmethod saving on Python 2. (issue #288)
+ * Add support for pickling "getset" descriptors (issue #290)
+
+-------------------------------------------------------------------
Old:
----
cloudpickle-1.2.1.tar.gz
New:
----
cloudpickle-1.2.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-cloudpickle.spec ++++++
--- /var/tmp/diff_new_pack.OuVfmM/_old 2019-09-30 15:56:33.149671137 +0200
+++ /var/tmp/diff_new_pack.OuVfmM/_new 2019-09-30 15:56:33.149671137 +0200
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-cloudpickle
-Version: 1.2.1
+Version: 1.2.2
Release: 0
Summary: Extended pickling support for Python objects
License: BSD-3-Clause
++++++ cloudpickle-1.2.1.tar.gz -> cloudpickle-1.2.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cloudpickle-1.2.1/PKG-INFO new/cloudpickle-1.2.2/PKG-INFO
--- old/cloudpickle-1.2.1/PKG-INFO 2019-06-10 21:56:01.000000000 +0200
+++ new/cloudpickle-1.2.2/PKG-INFO 2019-09-10 14:27:06.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: cloudpickle
-Version: 1.2.1
+Version: 1.2.2
Summary: Extended pickling support for Python objects
Home-page: https://github.com/cloudpipe/cloudpickle
Author: Cloudpipe
@@ -23,11 +23,15 @@
along with **functions and classes defined interactively** in the
`__main__` module (for instance in a script, a shell or a Jupyter notebook).
- **`cloudpickle` uses `pickle.HIGHEST_PROTOCOL` by default**: it is meant to
- send objects between processes running the **same version of Python**.
+ Cloudpickle can only be used to send objects between the **exact same version
+ of Python**.
Using `cloudpickle` for **long-term object storage is not supported and
- discouraged.**
+ strongly discouraged.**
+
+ **Security notice**: one should **only load pickle data from trusted sources** as
+ otherwise `pickle.load` can lead to arbitrary code execution resulting in a critical
+ security vulnerability.
Installation
@@ -89,6 +93,25 @@
PYTHONPATH='.:tests' py.test
+ Note about function Annotations
+ -------------------------------
+
+ Note that because of design issues `Python`'s `typing` module, `cloudpickle`
+ supports pickling type annotations of dynamic functions for `Python` 3.7 and
+ later. On `Python` 3.4, 3.5 and 3.6, those type annotations will be dropped
+ silently during pickling (example below):
+
+ ```python
+ >>> import typing
+ >>> import cloudpickle
+ >>> def f(x: typing.Union[list, int]):
+ ... return x
+ >>> f
+
+ >>> cloudpickle.loads(cloudpickle.dumps(f)) # drops f's annotations
+
+ ```
+
History
-------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cloudpickle-1.2.1/README.md new/cloudpickle-1.2.2/README.md
--- old/cloudpickle-1.2.1/README.md 2019-04-17 11:27:32.000000000 +0200
+++ new/cloudpickle-1.2.2/README.md 2019-09-10 14:19:13.000000000 +0200
@@ -15,11 +15,15 @@
along with **functions and classes defined interactively** in the
`__main__` module (for instance in a script, a shell or a Jupyter notebook).
-**`cloudpickle` uses `pickle.HIGHEST_PROTOCOL` by default**: it is meant to
-send objects between processes running the **same version of Python**.
+Cloudpickle can only be used to send objects between the **exact same version
+of Python**.
Using `cloudpickle` for **long-term object storage is not supported and
-discouraged.**
+strongly discouraged.**
+
+**Security notice**: one should **only load pickle data from trusted sources** as
+otherwise `pickle.load` can lead to arbitrary code execution resulting in a critical
+security vulnerability.
Installation
@@ -81,6 +85,25 @@
PYTHONPATH='.:tests' py.test
+Note about function Annotations
+-------------------------------
+
+Note that because of design issues `Python`'s `typing` module, `cloudpickle`
+supports pickling type annotations of dynamic functions for `Python` 3.7 and
+later. On `Python` 3.4, 3.5 and 3.6, those type annotations will be dropped
+silently during pickling (example below):
+
+```python
+>>> import typing
+>>> import cloudpickle
+>>> def f(x: typing.Union[list, int]):
+... return x
+>>> f
+
+>>> cloudpickle.loads(cloudpickle.dumps(f)) # drops f's annotations
+
+```
+
History
-------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cloudpickle-1.2.1/cloudpickle/__init__.py new/cloudpickle-1.2.2/cloudpickle/__init__.py
--- old/cloudpickle-1.2.1/cloudpickle/__init__.py 2019-06-10 21:55:30.000000000 +0200
+++ new/cloudpickle-1.2.2/cloudpickle/__init__.py 2019-09-10 14:19:34.000000000 +0200
@@ -8,4 +8,4 @@
if sys.version_info[:2] >= (3, 8):
from cloudpickle.cloudpickle_fast import CloudPickler, dumps, dump
-__version__ = '1.2.1'
+__version__ = '1.2.2'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cloudpickle-1.2.1/cloudpickle/cloudpickle.py new/cloudpickle-1.2.2/cloudpickle/cloudpickle.py
--- old/cloudpickle-1.2.1/cloudpickle/cloudpickle.py 2019-06-10 21:54:13.000000000 +0200
+++ new/cloudpickle-1.2.2/cloudpickle/cloudpickle.py 2019-09-10 14:19:13.000000000 +0200
@@ -274,41 +274,80 @@
return subimports
-def _make_cell_set_template_code():
- """Get the Python compiler to emit LOAD_FAST(arg); STORE_DEREF
-
- Notes
- -----
- In Python 3, we could use an easier function:
-
- .. code-block:: python
-
- def f():
- cell = None
+def cell_set(cell, value):
+ """Set the value of a closure cell.
- def _stub(value):
- nonlocal cell
- cell = value
+ The point of this function is to set the cell_contents attribute of a cell
+ after its creation. This operation is necessary in case the cell contains a
+ reference to the function the cell belongs to, as when calling the
+ function's constructor
+ ``f = types.FunctionType(code, globals, name, argdefs, closure)``,
+ closure will not be able to contain the yet-to-be-created f.
+
+ In Python3.7, cell_contents is writeable, so setting the contents of a cell
+ can be done simply using
+ >>> cell.cell_contents = value
+
+ In earlier Python3 versions, the cell_contents attribute of a cell is read
+ only, but this limitation can be worked around by leveraging the Python 3
+ ``nonlocal`` keyword.
+
+ In Python2 however, this attribute is read only, and there is no
+ ``nonlocal`` keyword. For this reason, we need to come up with more
+ complicated hacks to set this attribute.
+
+ The chosen approach is to create a function with a STORE_DEREF opcode,
+ which sets the content of a closure variable. Typically:
+
+ >>> def inner(value):
+ ... lambda: cell # the lambda makes cell a closure
+ ... cell = value # cell is a closure, so this triggers a STORE_DEREF
+
+ (Note that in Python2, A STORE_DEREF can never be triggered from an inner
+ function. The function g for example here
+ >>> def f(var):
+ ... def g():
+ ... var += 1
+ ... return g
+
+ will not modify the closure variable ``var```inplace, but instead try to
+ load a local variable var and increment it. As g does not assign the local
+ variable ``var`` any initial value, calling f(1)() will fail at runtime.)
+
+ Our objective is to set the value of a given cell ``cell``. So we need to
+ somewhat reference our ``cell`` object into the ``inner`` function so that
+ this object (and not the smoke cell of the lambda function) gets affected
+ by the STORE_DEREF operation.
+
+ In inner, ``cell`` is referenced as a cell variable (an enclosing variable
+ that is referenced by the inner function). If we create a new function
+ cell_set with the exact same code as ``inner``, but with ``cell`` marked as
+ a free variable instead, the STORE_DEREF will be applied on its closure -
+ ``cell``, which we can specify explicitly during construction! The new
+ cell_set variable thus actually sets the contents of a specified cell!
+
+ Note: we do not make use of the ``nonlocal`` keyword to set the contents of
+ a cell in early python3 versions to limit possible syntax errors in case
+ test and checker libraries decide to parse the whole file.
+ """
- return _stub
+ if sys.version_info[:2] >= (3, 7): # pragma: no branch
+ cell.cell_contents = value
+ else:
+ _cell_set = types.FunctionType(
+ _cell_set_template_code, {}, '_cell_set', (), (cell,),)
+ _cell_set(value)
- _cell_set_template_code = f().__code__
- This function is _only_ a LOAD_FAST(arg); STORE_DEREF, but that is
- invalid syntax on Python 2. If we use this function we also don't need
- to do the weird freevars/cellvars swap below
- """
- def inner(value):
- lambda: cell # make ``cell`` a closure so that we get a STORE_DEREF
+def _make_cell_set_template_code():
+ def _cell_set_factory(value):
+ lambda: cell
cell = value
- co = inner.__code__
+ co = _cell_set_factory.__code__
- # NOTE: we are marking the cell variable as a free variable intentionally
- # so that we simulate an inner function instead of the outer function. This
- # is what gives us the ``nonlocal`` behavior in a Python 2 compatible way.
if PY2: # pragma: no branch
- return types.CodeType(
+ _cell_set_template_code = types.CodeType(
co.co_argcount,
co.co_nlocals,
co.co_stacksize,
@@ -321,62 +360,32 @@
co.co_name,
co.co_firstlineno,
co.co_lnotab,
- co.co_cellvars, # this is the trickery
- (),
+ co.co_cellvars, # co_freevars is initialized with co_cellvars
+ (), # co_cellvars is made empty
)
else:
- if hasattr(types.CodeType, "co_posonlyargcount"): # pragma: no branch
- return types.CodeType(
- co.co_argcount,
- co.co_posonlyargcount, # Python3.8 with PEP570
- co.co_kwonlyargcount,
- co.co_nlocals,
- co.co_stacksize,
- co.co_flags,
- co.co_code,
- co.co_consts,
- co.co_names,
- co.co_varnames,
- co.co_filename,
- co.co_name,
- co.co_firstlineno,
- co.co_lnotab,
- co.co_cellvars, # this is the trickery
- (),
- )
- else:
- return types.CodeType(
- co.co_argcount,
- co.co_kwonlyargcount,
- co.co_nlocals,
- co.co_stacksize,
- co.co_flags,
- co.co_code,
- co.co_consts,
- co.co_names,
- co.co_varnames,
- co.co_filename,
- co.co_name,
- co.co_firstlineno,
- co.co_lnotab,
- co.co_cellvars, # this is the trickery
- (),
- )
-
-_cell_set_template_code = _make_cell_set_template_code()
-
+ _cell_set_template_code = types.CodeType(
+ co.co_argcount,
+ co.co_kwonlyargcount, # Python 3 only argument
+ co.co_nlocals,
+ co.co_stacksize,
+ co.co_flags,
+ co.co_code,
+ co.co_consts,
+ co.co_names,
+ co.co_varnames,
+ co.co_filename,
+ co.co_name,
+ co.co_firstlineno,
+ co.co_lnotab,
+ co.co_cellvars, # co_freevars is initialized with co_cellvars
+ (), # co_cellvars is made empty
+ )
+ return _cell_set_template_code
-def cell_set(cell, value):
- """Set the value of a closure cell.
- """
- return types.FunctionType(
- _cell_set_template_code,
- {},
- '_cell_set_inner',
- (),
- (cell,),
- )(value)
+if sys.version_info[:2] < (3, 7):
+ _cell_set_template_code = _make_cell_set_template_code()
# relevant opcodes
STORE_GLOBAL = opcode.opmap['STORE_GLOBAL']
@@ -738,7 +747,9 @@
'doc': func.__doc__,
'_cloudpickle_submodules': submodules
}
- if hasattr(func, '__annotations__') and sys.version_info >= (3, 4):
+ if hasattr(func, '__annotations__') and sys.version_info >= (3, 7):
+ # Although annotations were added in Python3.4, It is not possible
+ # to properly pickle them until Python3.7. (See #193)
state['annotations'] = func.__annotations__
if hasattr(func, '__qualname__'):
state['qualname'] = func.__qualname__
@@ -839,6 +850,11 @@
method_descriptor = type(str.upper)
dispatch[method_descriptor] = save_builtin_function_or_method
+ def save_getset_descriptor(self, obj):
+ return self.save_reduce(getattr, (obj.__objclass__, obj.__name__))
+
+ dispatch[types.GetSetDescriptorType] = save_getset_descriptor
+
def save_global(self, obj, name=None, pack=struct.pack):
"""
Save a "global".
@@ -873,8 +889,9 @@
if PY3: # pragma: no branch
self.save_reduce(types.MethodType, (obj.__func__, obj.__self__), obj=obj)
else:
- self.save_reduce(types.MethodType, (obj.__func__, obj.__self__, obj.__self__.__class__),
- obj=obj)
+ self.save_reduce(
+ types.MethodType,
+ (obj.__func__, obj.__self__, type(obj.__self__)), obj=obj)
dispatch[types.MethodType] = save_instancemethod
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cloudpickle-1.2.1/cloudpickle/cloudpickle_fast.py new/cloudpickle-1.2.2/cloudpickle/cloudpickle_fast.py
--- old/cloudpickle-1.2.1/cloudpickle/cloudpickle_fast.py 2019-06-07 19:01:53.000000000 +0200
+++ new/cloudpickle-1.2.2/cloudpickle/cloudpickle_fast.py 2019-08-02 21:50:49.000000000 +0200
@@ -260,6 +260,10 @@
return _file_reconstructor, (retval,)
+def _getset_descriptor_reduce(obj):
+ return getattr, (obj.__objclass__, obj.__name__)
+
+
def _mappingproxy_reduce(obj):
return types.MappingProxyType, (dict(obj),)
@@ -405,6 +409,7 @@
dispatch[staticmethod] = _classmethod_reduce
dispatch[types.CellType] = _cell_reduce
dispatch[types.CodeType] = _code_reduce
+ dispatch[types.GetSetDescriptorType] = _getset_descriptor_reduce
dispatch[types.ModuleType] = _module_reduce
dispatch[types.MethodType] = _method_reduce
dispatch[types.MappingProxyType] = _mappingproxy_reduce
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cloudpickle-1.2.1/cloudpickle.egg-info/PKG-INFO new/cloudpickle-1.2.2/cloudpickle.egg-info/PKG-INFO
--- old/cloudpickle-1.2.1/cloudpickle.egg-info/PKG-INFO 2019-06-10 21:56:01.000000000 +0200
+++ new/cloudpickle-1.2.2/cloudpickle.egg-info/PKG-INFO 2019-09-10 14:27:06.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: cloudpickle
-Version: 1.2.1
+Version: 1.2.2
Summary: Extended pickling support for Python objects
Home-page: https://github.com/cloudpipe/cloudpickle
Author: Cloudpipe
@@ -23,11 +23,15 @@
along with **functions and classes defined interactively** in the
`__main__` module (for instance in a script, a shell or a Jupyter notebook).
- **`cloudpickle` uses `pickle.HIGHEST_PROTOCOL` by default**: it is meant to
- send objects between processes running the **same version of Python**.
+ Cloudpickle can only be used to send objects between the **exact same version
+ of Python**.
Using `cloudpickle` for **long-term object storage is not supported and
- discouraged.**
+ strongly discouraged.**
+
+ **Security notice**: one should **only load pickle data from trusted sources** as
+ otherwise `pickle.load` can lead to arbitrary code execution resulting in a critical
+ security vulnerability.
Installation
@@ -89,6 +93,25 @@
PYTHONPATH='.:tests' py.test
+ Note about function Annotations
+ -------------------------------
+
+ Note that because of design issues `Python`'s `typing` module, `cloudpickle`
+ supports pickling type annotations of dynamic functions for `Python` 3.7 and
+ later. On `Python` 3.4, 3.5 and 3.6, those type annotations will be dropped
+ silently during pickling (example below):
+
+ ```python
+ >>> import typing
+ >>> import cloudpickle
+ >>> def f(x: typing.Union[list, int]):
+ ... return x
+ >>> f
+
+ >>> cloudpickle.loads(cloudpickle.dumps(f)) # drops f's annotations
+
+ ```
+
History
-------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cloudpickle-1.2.1/tests/cloudpickle_test.py new/cloudpickle-1.2.2/tests/cloudpickle_test.py
--- old/cloudpickle-1.2.1/tests/cloudpickle_test.py 2019-06-10 21:54:13.000000000 +0200
+++ new/cloudpickle-1.2.2/tests/cloudpickle_test.py 2019-09-10 14:19:13.000000000 +0200
@@ -431,6 +431,15 @@
self.assertEqual(A.test_sm(), "sm")
self.assertEqual(A.test_cm(), "cm")
+ def test_bound_classmethod(self):
+ class A:
+ @classmethod
+ def test_cm(cls):
+ return "cm"
+
+ A.test_cm = pickle_depickle(A.test_cm, protocol=self.protocol)
+ self.assertEqual(A.test_cm(), "cm")
+
def test_method_descriptors(self):
f = pickle_depickle(str.upper)
self.assertEqual(f('abc'), 'ABC')
@@ -661,10 +670,9 @@
# builtin function from the __builtin__ module
assert pickle_depickle(zip, protocol=self.protocol) is zip
- from sys import getcheckinterval
+ from os import mkdir
# builtin function from a "regular" module
- assert pickle_depickle(
- getcheckinterval, protocol=self.protocol) is getcheckinterval
+ assert pickle_depickle(mkdir, protocol=self.protocol) is mkdir
@pytest.mark.skipif(platform.python_implementation() == 'PyPy' and
sys.version_info[:2] == (3, 5),
@@ -975,6 +983,11 @@
# logging.Logger object
self.check_logger('cloudpickle.dummy_test_logger')
+ def test_getset_descriptor(self):
+ assert isinstance(float.real, types.GetSetDescriptorType)
+ depickled_descriptor = pickle_depickle(float.real)
+ self.assertIs(depickled_descriptor, float.real)
+
def test_abc(self):
@abc.abstractmethod
@@ -1607,9 +1620,9 @@
self.assertEqual(f2.__doc__, f.__doc__)
- @unittest.skipIf(sys.version_info < (3, 4),
- """This syntax won't work on py2 and pickling annotations
- isn't supported for py34 and below.""")
+ @unittest.skipIf(sys.version_info < (3, 7),
+ "This syntax won't work on py2 and pickling annotations "
+ "isn't supported for py37 and below.")
def test_wraps_preserves_function_annotations(self):
from functools import wraps
@@ -1626,6 +1639,16 @@
self.assertEqual(f2.__annotations__, f.__annotations__)
+ @unittest.skipIf(sys.version_info < (3, 7),
+ """This syntax won't work on py2 and pickling annotations
+ isn't supported for py37 and below.""")
+ def test_type_hint(self):
+ # Try to pickle compound typing constructs. This would typically fail
+ # on Python < 3.7 (See #193)
+ import typing
+ t = typing.Union[list, int]
+ assert pickle_depickle(t) == t
+
def test_instance_with_slots(self):
for slots in [["registered_attribute"], "registered_attribute"]:
class ClassWithSlots(object):