Hello community,

here is the log from the commit of package python-cloudpickle for 
openSUSE:Factory checked in at 2020-04-04 12:20:34
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-cloudpickle (Old)
 and      /work/SRC/openSUSE:Factory/.python-cloudpickle.new.3248 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-cloudpickle"

Sat Apr  4 12:20:34 2020 rev:11 rq:790248 version:1.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-cloudpickle/python-cloudpickle.changes    
2020-03-27 00:25:28.872262390 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-cloudpickle.new.3248/python-cloudpickle.changes
  2020-04-04 12:20:36.667685318 +0200
@@ -1,0 +2,7 @@
+Tue Mar 31 14:59:31 UTC 2020 - Marketa Calabkova <mcalabk...@suse.com>
+
+- Update to version 1.3.0
+  * mostly bugfix release
+  * Add support for out-of-band pickling (Python 3.8 and later).
+
+-------------------------------------------------------------------

Old:
----
  cloudpickle-1.2.2.tar.gz

New:
----
  cloudpickle-1.3.0.tar.gz

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

Other differences:
------------------
++++++ python-cloudpickle.spec ++++++
--- /var/tmp/diff_new_pack.3GioyF/_old  2020-04-04 12:20:37.267685833 +0200
+++ /var/tmp/diff_new_pack.3GioyF/_new  2020-04-04 12:20:37.271685836 +0200
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %bcond_without python2
 Name:           python-cloudpickle
-Version:        1.2.2
+Version:        1.3.0
 Release:        0
 Summary:        Extended pickling support for Python objects
 License:        BSD-3-Clause

++++++ cloudpickle-1.2.2.tar.gz -> cloudpickle-1.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudpickle-1.2.2/PKG-INFO 
new/cloudpickle-1.3.0/PKG-INFO
--- old/cloudpickle-1.2.2/PKG-INFO      2019-09-10 14:27:06.000000000 +0200
+++ new/cloudpickle-1.3.0/PKG-INFO      2020-02-10 15:30:46.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: cloudpickle
-Version: 1.2.2
+Version: 1.3.0
 Summary: Extended pickling support for Python objects
 Home-page: https://github.com/cloudpipe/cloudpickle
 Author: Cloudpipe
@@ -8,8 +8,7 @@
 License: BSD 3-Clause License
 Description: # cloudpickle
         
-        [![Build 
Status](https://travis-ci.org/cloudpipe/cloudpickle.svg?branch=master
-            )](https://travis-ci.org/cloudpipe/cloudpickle)
+        
[![github-actions](https://github.com/cloudpipe/cloudpickle/workflows/Automated%20Tests/badge.svg)](https://github.com/cloudpipe/cloudpickle/actions)
         
[![codecov.io](https://codecov.io/github/cloudpipe/cloudpickle/coverage.svg?branch=master)](https://codecov.io/github/cloudpipe/cloudpickle?branch=master)
         
         `cloudpickle` makes it possible to serialize Python constructs not 
supported
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudpickle-1.2.2/README.md 
new/cloudpickle-1.3.0/README.md
--- old/cloudpickle-1.2.2/README.md     2019-09-10 14:19:13.000000000 +0200
+++ new/cloudpickle-1.3.0/README.md     2020-02-10 15:13:49.000000000 +0100
@@ -1,7 +1,6 @@
 # cloudpickle
 
-[![Build Status](https://travis-ci.org/cloudpipe/cloudpickle.svg?branch=master
-    )](https://travis-ci.org/cloudpipe/cloudpickle)
+[![github-actions](https://github.com/cloudpipe/cloudpickle/workflows/Automated%20Tests/badge.svg)](https://github.com/cloudpipe/cloudpickle/actions)
 
[![codecov.io](https://codecov.io/github/cloudpipe/cloudpickle/coverage.svg?branch=master)](https://codecov.io/github/cloudpipe/cloudpickle?branch=master)
 
 `cloudpickle` makes it possible to serialize Python constructs not supported
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudpickle-1.2.2/cloudpickle/__init__.py 
new/cloudpickle-1.3.0/cloudpickle/__init__.py
--- old/cloudpickle-1.2.2/cloudpickle/__init__.py       2019-09-10 
14:19:34.000000000 +0200
+++ new/cloudpickle-1.3.0/cloudpickle/__init__.py       2020-02-10 
15:15:40.000000000 +0100
@@ -8,4 +8,4 @@
 if sys.version_info[:2] >= (3, 8):
     from cloudpickle.cloudpickle_fast import CloudPickler, dumps, dump
 
-__version__ = '1.2.2'
+__version__ = '1.3.0'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudpickle-1.2.2/cloudpickle/cloudpickle.py 
new/cloudpickle-1.3.0/cloudpickle/cloudpickle.py
--- old/cloudpickle-1.2.2/cloudpickle/cloudpickle.py    2019-09-10 
14:19:13.000000000 +0200
+++ new/cloudpickle-1.3.0/cloudpickle/cloudpickle.py    2020-02-10 
15:13:49.000000000 +0100
@@ -42,6 +42,7 @@
 """
 from __future__ import print_function
 
+import abc
 import dis
 from functools import partial
 import io
@@ -90,17 +91,18 @@
         from cStringIO import StringIO
     except ImportError:
         from StringIO import StringIO
+    import __builtin__ as builtins
     string_types = (basestring,)  # noqa
     PY3 = False
     PY2 = True
 else:
-    types.ClassType = type
     from pickle import _Pickler as Pickler
     from io import BytesIO as StringIO
     string_types = (str,)
     PY3 = True
     PY2 = False
     from importlib._bootstrap import _find_spec
+    import builtins
 
 _extract_code_globals_cache = weakref.WeakKeyDictionary()
 
@@ -151,10 +153,17 @@
     module_name = getattr(obj, '__module__', None)
     if module_name is not None:
         return module_name
-    # Protect the iteration by using a list copy of sys.modules against dynamic
-    # modules that trigger imports of other modules upon calls to getattr.
-    for module_name, module in list(sys.modules.items()):
-        if module_name == '__main__' or module is None:
+    # Protect the iteration by using a copy of sys.modules against dynamic
+    # modules that trigger imports of other modules upon calls to getattr or
+    # other threads importing at the same time.
+    for module_name, module in sys.modules.copy().items():
+        # Some modules such as coverage can inject non-module objects inside
+        # sys.modules
+        if (
+                module_name == '__main__' or
+                module is None or
+                not isinstance(module, types.ModuleType)
+        ):
             continue
         try:
             if _getattribute(module, name)[0] is obj:
@@ -503,6 +512,7 @@
         Save a module as an import
         """
         if _is_dynamic(obj):
+            obj.__dict__.pop('__builtins__', None)
             self.save_reduce(dynamic_subimport, (obj.__name__, vars(obj)),
                              obj=obj)
         else:
@@ -617,21 +627,33 @@
         clsdict = _extract_class_dict(obj)
         clsdict.pop('__weakref__', None)
 
-        # For ABCMeta in python3.7+, remove _abc_impl as it is not picklable.
-        # This is a fix which breaks the cache but this only makes the first
-        # calls to issubclass slower.
-        if "_abc_impl" in clsdict:
-            import abc
-            (registry, _, _, _) = abc._get_dump(obj)
-            clsdict["_abc_impl"] = [subclass_weakref()
-                                    for subclass_weakref in registry]
+        if issubclass(type(obj), abc.ABCMeta):
+            # If obj is an instance of an ABCMeta subclass, dont pickle the
+            # cache/negative caches populated during isinstance/issubclass
+            # checks, but pickle the list of registered subclasses of obj.
+            clsdict.pop('_abc_cache', None)
+            clsdict.pop('_abc_negative_cache', None)
+            clsdict.pop('_abc_negative_cache_version', None)
+            registry = clsdict.pop('_abc_registry', None)
+            if registry is None:
+                # in Python3.7+, the abc caches and registered subclasses of a
+                # class are bundled into the single _abc_impl attribute
+                clsdict.pop('_abc_impl', None)
+                (registry, _, _, _) = abc._get_dump(obj)
+
+                clsdict["_abc_impl"] = [subclass_weakref()
+                                        for subclass_weakref in registry]
+            else:
+                # In the above if clause, registry is a set of weakrefs -- in
+                # this case, registry is a WeakSet
+                clsdict["_abc_impl"] = [type_ for type_ in registry]
 
         # On PyPy, __doc__ is a readonly attribute, so we need to include it in
         # the initial skeleton class.  This is safe because we know that the
         # doc can't participate in a cycle with the original class.
         type_kwargs = {'__doc__': clsdict.pop('__doc__', None)}
 
-        if hasattr(obj, "__slots__"):
+        if "__slots__" in clsdict:
             type_kwargs['__slots__'] = obj.__slots__
             # pickle string length optimization: member descriptors of obj are
             # created automatically from obj's __slots__ attribute, no need to
@@ -879,7 +901,8 @@
             Pickler.save_global(self, obj, name=name)
 
     dispatch[type] = save_global
-    dispatch[types.ClassType] = save_global
+    if PY2:
+        dispatch[types.ClassType] = save_global
 
     def save_instancemethod(self, obj):
         # Memoization rarely is ever useful due to python bounding
@@ -1142,6 +1165,7 @@
 def dynamic_subimport(name, vars):
     mod = types.ModuleType(name)
     mod.__dict__.update(vars)
+    mod.__dict__['__builtins__'] = builtins.__dict__
     return mod
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudpickle-1.2.2/cloudpickle/cloudpickle_fast.py 
new/cloudpickle-1.3.0/cloudpickle/cloudpickle_fast.py
--- old/cloudpickle-1.2.2/cloudpickle/cloudpickle_fast.py       2019-08-02 
21:50:49.000000000 +0200
+++ new/cloudpickle-1.3.0/cloudpickle/cloudpickle_fast.py       2020-02-10 
15:13:49.000000000 +0100
@@ -34,7 +34,7 @@
 
 
 # Shorthands similar to pickle.dump/pickle.dumps
-def dump(obj, file, protocol=None):
+def dump(obj, file, protocol=None, buffer_callback=None):
     """Serialize obj as bytes streamed into file
 
     protocol defaults to cloudpickle.DEFAULT_PROTOCOL which is an alias to
@@ -44,10 +44,10 @@
     Set protocol=pickle.DEFAULT_PROTOCOL instead if you need to ensure
     compatibility with older versions of Python.
     """
-    CloudPickler(file, protocol=protocol).dump(obj)
+    CloudPickler(file, protocol=protocol, 
buffer_callback=buffer_callback).dump(obj)
 
 
-def dumps(obj, protocol=None):
+def dumps(obj, protocol=None, buffer_callback=None):
     """Serialize obj as a string of bytes allocated in memory
 
     protocol defaults to cloudpickle.DEFAULT_PROTOCOL which is an alias to
@@ -58,7 +58,7 @@
     compatibility with older versions of Python.
     """
     with io.BytesIO() as file:
-        cp = CloudPickler(file, protocol=protocol)
+        cp = CloudPickler(file, protocol=protocol, 
buffer_callback=buffer_callback)
         cp.dump(obj)
         return file.getvalue()
 
@@ -68,7 +68,7 @@
 
 def _class_getnewargs(obj):
     type_kwargs = {}
-    if hasattr(obj, "__slots__"):
+    if "__slots__" in obj.__dict__:
         type_kwargs["__slots__"] = obj.__slots__
 
     __dict__ = obj.__dict__.get('__dict__', None)
@@ -136,14 +136,16 @@
     clsdict = _extract_class_dict(obj)
     clsdict.pop('__weakref__', None)
 
-    # For ABCMeta in python3.7+, remove _abc_impl as it is not picklable.
-    # This is a fix which breaks the cache but this only makes the first
-    # calls to issubclass slower.
-    if "_abc_impl" in clsdict:
+    if issubclass(type(obj), abc.ABCMeta):
+        # If obj is an instance of an ABCMeta subclass, dont pickle the
+        # cache/negative caches populated during isinstance/issubclass
+        # checks, but pickle the list of registered subclasses of obj.
+        clsdict.pop('_abc_impl', None)
         (registry, _, _, _) = abc._get_dump(obj)
         clsdict["_abc_impl"] = [subclass_weakref()
                                 for subclass_weakref in registry]
-    if hasattr(obj, "__slots__"):
+
+    if "__slots__" in clsdict:
         # pickle string length optimization: member descriptors of obj are
         # created automatically from obj's __slots__ attribute, no need to
         # save them in obj's state
@@ -274,6 +276,7 @@
 
 def _module_reduce(obj):
     if _is_dynamic(obj):
+        obj.__dict__.pop('__builtins__', None)
         return dynamic_subimport, (obj.__name__, vars(obj))
     else:
         return subimport, (obj.__name__,)
@@ -291,6 +294,10 @@
     return logging.getLogger, ()
 
 
+def _property_reduce(obj):
+    return property, (obj.fget, obj.fset, obj.fdel, obj.__doc__)
+
+
 def _weakset_reduce(obj):
     return weakref.WeakSet, (list(obj),)
 
@@ -406,6 +413,7 @@
     dispatch[logging.Logger] = _logger_reduce
     dispatch[logging.RootLogger] = _root_logger_reduce
     dispatch[memoryview] = _memoryview_reduce
+    dispatch[property] = _property_reduce
     dispatch[staticmethod] = _classmethod_reduce
     dispatch[types.CellType] = _cell_reduce
     dispatch[types.CodeType] = _code_reduce
@@ -415,10 +423,10 @@
     dispatch[types.MappingProxyType] = _mappingproxy_reduce
     dispatch[weakref.WeakSet] = _weakset_reduce
 
-    def __init__(self, file, protocol=None):
+    def __init__(self, file, protocol=None, buffer_callback=None):
         if protocol is None:
             protocol = DEFAULT_PROTOCOL
-        Pickler.__init__(self, file, protocol=protocol)
+        Pickler.__init__(self, file, protocol=protocol, 
buffer_callback=buffer_callback)
         # map functions __globals__ attribute ids, to ensure that functions
         # sharing the same global namespace at pickling time also share their
         # global namespace at unpickling time.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudpickle-1.2.2/cloudpickle.egg-info/PKG-INFO 
new/cloudpickle-1.3.0/cloudpickle.egg-info/PKG-INFO
--- old/cloudpickle-1.2.2/cloudpickle.egg-info/PKG-INFO 2019-09-10 
14:27:06.000000000 +0200
+++ new/cloudpickle-1.3.0/cloudpickle.egg-info/PKG-INFO 2020-02-10 
15:30:46.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: cloudpickle
-Version: 1.2.2
+Version: 1.3.0
 Summary: Extended pickling support for Python objects
 Home-page: https://github.com/cloudpipe/cloudpickle
 Author: Cloudpipe
@@ -8,8 +8,7 @@
 License: BSD 3-Clause License
 Description: # cloudpickle
         
-        [![Build 
Status](https://travis-ci.org/cloudpipe/cloudpickle.svg?branch=master
-            )](https://travis-ci.org/cloudpipe/cloudpickle)
+        
[![github-actions](https://github.com/cloudpipe/cloudpickle/workflows/Automated%20Tests/badge.svg)](https://github.com/cloudpipe/cloudpickle/actions)
         
[![codecov.io](https://codecov.io/github/cloudpipe/cloudpickle/coverage.svg?branch=master)](https://codecov.io/github/cloudpipe/cloudpickle?branch=master)
         
         `cloudpickle` makes it possible to serialize Python constructs not 
supported
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/cloudpickle-1.2.2/tests/cloudpickle_test.py 
new/cloudpickle-1.3.0/tests/cloudpickle_test.py
--- old/cloudpickle-1.2.2/tests/cloudpickle_test.py     2019-09-10 
14:19:13.000000000 +0200
+++ new/cloudpickle-1.3.0/tests/cloudpickle_test.py     2020-02-10 
15:13:49.000000000 +0100
@@ -42,7 +42,7 @@
 import cloudpickle
 from cloudpickle.cloudpickle import _is_dynamic
 from cloudpickle.cloudpickle import _make_empty_cell, cell_set
-from cloudpickle.cloudpickle import _extract_class_dict
+from cloudpickle.cloudpickle import _extract_class_dict, _whichmodule
 
 from .testutils import subprocess_pickle_echo
 from .testutils import assert_run_python_script
@@ -108,6 +108,28 @@
     def tearDown(self):
         shutil.rmtree(self.tmpdir)
 
+    @pytest.mark.skipif(
+            platform.python_implementation() != "CPython" or
+            (sys.version_info >= (3, 8, 0) and sys.version_info < (3, 8, 2)),
+            reason="Underlying bug fixed upstream starting Python 3.8.2")
+    def test_reducer_override_reference_cycle(self):
+        # Early versions of Python 3.8 introduced a reference cycle between a
+        # Pickler and it's reducer_override method. Because a Pickler
+        # object references every object it has pickled through its memo, this
+        # cycle prevented the garbage-collection of those external pickled
+        # objects. See #327 as well as https://bugs.python.org/issue39492
+        # This bug was fixed in Python 3.8.2, but is still present using
+        # cloudpickle and Python 3.8.0/1, hence the skipif directive.
+        class MyClass:
+            pass
+
+        my_object = MyClass()
+        wr = weakref.ref(my_object)
+
+        cloudpickle.dumps(my_object)
+        del my_object
+        assert wr() is None, "'del'-ed my_object has not been collected"
+
     def test_itemgetter(self):
         d = range(10)
         getter = itemgetter(1)
@@ -528,6 +550,46 @@
         finally:
             os.unlink(pickled_func_path)
 
+    def test_dynamic_module_with_unpicklable_builtin(self):
+        # Reproducer of https://github.com/cloudpipe/cloudpickle/issues/316
+        # Some modules such as scipy inject some unpicklable objects into the
+        # __builtins__ module, which appears in every module's __dict__ under
+        # the '__builtins__' key. In such cases, cloudpickle used to fail
+        # when pickling dynamic modules.
+        class UnpickleableObject(object):
+            def __reduce__(self):
+                raise ValueError('Unpicklable object')
+
+        mod = types.ModuleType("mod")
+
+        exec('f = lambda x: abs(x)', mod.__dict__)
+        assert mod.f(-1) == 1
+        assert '__builtins__' in mod.__dict__
+
+        unpicklable_obj = UnpickleableObject()
+        with pytest.raises(ValueError):
+            cloudpickle.dumps(unpicklable_obj)
+
+        # Emulate the behavior of scipy by injecting an unpickleable object
+        # into mod's builtins.
+        # The __builtins__ entry of mod's __dict__ can either be the
+        # __builtins__ module, or the __builtins__ module's __dict__. #316
+        # happens only in the latter case.
+        if isinstance(mod.__dict__['__builtins__'], dict):
+            mod.__dict__['__builtins__']['unpickleable_obj'] = unpicklable_obj
+        elif isinstance(mod.__dict__['__builtins__'], types.ModuleType):
+            mod.__dict__['__builtins__'].unpickleable_obj = unpicklable_obj
+
+        depickled_mod = pickle_depickle(mod, protocol=self.protocol)
+        assert '__builtins__' in depickled_mod.__dict__
+
+        if isinstance(depickled_mod.__dict__['__builtins__'], dict):
+            assert "abs" in depickled_mod.__builtins__
+        elif isinstance(
+                depickled_mod.__dict__['__builtins__'], types.ModuleType):
+            assert hasattr(depickled_mod.__builtins__, "abs")
+        assert depickled_mod.f(-1) == 1
+
     def test_load_dynamic_module_in_grandchild_process(self):
         # Make sure that when loaded, a dynamic module preserves its dynamic
         # property. Otherwise, this will lead to an ImportError if pickled in
@@ -988,6 +1050,32 @@
         depickled_descriptor = pickle_depickle(float.real)
         self.assertIs(depickled_descriptor, float.real)
 
+    def test_abc_cache_not_pickled(self):
+        # cloudpickle issue #302: make sure that cloudpickle does not pickle
+        # the caches populated during instance/subclass checks of abc.ABCMeta
+        # instances.
+        MyClass = abc.ABCMeta('MyClass', (), {})
+
+        class MyUnrelatedClass:
+            pass
+
+        class MyRelatedClass:
+            pass
+
+        MyClass.register(MyRelatedClass)
+
+        assert not issubclass(MyUnrelatedClass, MyClass)
+        assert issubclass(MyRelatedClass, MyClass)
+
+        s = cloudpickle.dumps(MyClass)
+
+        assert b"MyUnrelatedClass" not in s
+        assert b"MyRelatedClass" in s
+
+        depickled_class = cloudpickle.loads(s)
+        assert not issubclass(MyUnrelatedClass, depickled_class)
+        assert issubclass(MyRelatedClass, depickled_class)
+
     def test_abc(self):
 
         @abc.abstractmethod
@@ -1048,35 +1136,94 @@
 
         self.assertEqual(set(weakset), {depickled1, depickled2})
 
-    def test_faulty_module(self):
-        for module_name in ['_missing_module', None]:
-            class FaultyModule(object):
-                def __getattr__(self, name):
-                    # This throws an exception while looking up within
-                    # pickle.whichmodule or getattr(module, name, None)
-                    raise Exception()
+    def test_non_module_object_passing_whichmodule_test(self):
+        # https://github.com/cloudpipe/cloudpickle/pull/326: cloudpickle should
+        # not try to instrospect non-modules object when trying to discover the
+        # module of a function/class. This happenened because codecov injects
+        # tuples (and not modules) into sys.modules, but type-checks were not
+        # carried out on the entries of sys.modules, causing cloupdickle to
+        # then error in unexpected ways
+        def func(x):
+            return x ** 2
 
-            class Foo(object):
-                __module__ = module_name
+        # Trigger a loop during the execution of whichmodule(func) by
+        # explicitly setting the function's module to None
+        func.__module__ = None
 
-                def foo(self):
-                    return "it works!"
+        class NonModuleObject(object):
+            def __getattr__(self, name):
+                # We whitelist func so that a _whichmodule(func, None) call 
returns
+                # the NonModuleObject instance if a type check on the entries
+                # of sys.modules is not carried out, but manipulating this
+                # instance thinking it really is a module later on in the
+                # pickling process of func errors out
+                if name == 'func':
+                    return func
+                else:
+                    raise AttributeError
+
+        non_module_object = NonModuleObject()
+
+        assert func(2) == 4
+        assert func is non_module_object.func
+
+        # Any manipulation of non_module_object relying on attribute access
+        # will raise an Exception
+        with pytest.raises(AttributeError):
+            _is_dynamic(non_module_object)
+
+        try:
+            sys.modules['NonModuleObject'] = non_module_object
 
-            def foo():
-                return "it works!"
+            func_module_name = _whichmodule(func, None)
+            assert func_module_name != 'NonModuleObject'
+            assert func_module_name is None
 
-            foo.__module__ = module_name
+            depickled_func = pickle_depickle(func, protocol=self.protocol)
+            assert depickled_func(2) == 4
 
-            sys.modules["_faulty_module"] = FaultyModule()
-            try:
-                # Test whichmodule in save_global.
-                self.assertEqual(pickle_depickle(Foo()).foo(), "it works!")
-
-                # Test whichmodule in save_function.
-                cloned = pickle_depickle(foo, protocol=self.protocol)
-                self.assertEqual(cloned(), "it works!")
-            finally:
-                sys.modules.pop("_faulty_module", None)
+        finally:
+            sys.modules.pop('NonModuleObject')
+
+    def test_unrelated_faulty_module(self):
+        # Check that pickling a dynamically defined function or class does not
+        # fail when introspecting the currently loaded modules in sys.modules
+        # as long as those faulty modules are unrelated to the class or
+        # function we are currently pickling.
+        for base_class in (object, types.ModuleType):
+            for module_name in ['_missing_module', None]:
+                class FaultyModule(base_class):
+                    def __getattr__(self, name):
+                        # This throws an exception while looking up within
+                        # pickle.whichmodule or getattr(module, name, None)
+                        raise Exception()
+
+                class Foo(object):
+                    __module__ = module_name
+
+                    def foo(self):
+                        return "it works!"
+
+                def foo():
+                    return "it works!"
+
+                foo.__module__ = module_name
+
+                if base_class is types.ModuleType:  # noqa
+                    faulty_module = FaultyModule('_faulty_module')
+                else:
+                    faulty_module = FaultyModule()
+                sys.modules["_faulty_module"] = faulty_module
+
+                try:
+                    # Test whichmodule in save_global.
+                    self.assertEqual(pickle_depickle(Foo()).foo(), "it works!")
+
+                    # Test whichmodule in save_function.
+                    cloned = pickle_depickle(foo, protocol=self.protocol)
+                    self.assertEqual(cloned(), "it works!")
+                finally:
+                    sys.modules.pop("_faulty_module", None)
 
     def test_dynamic_pytest_module(self):
         # Test case for pull request 
https://github.com/cloudpipe/cloudpickle/pull/116
@@ -1119,6 +1266,52 @@
         cloned = pickle_depickle(func, protocol=self.protocol)
         self.assertEqual(cloned.__qualname__, func.__qualname__)
 
+    def test_property(self):
+        # Note that the @property decorator only has an effect on new-style
+        # classes.
+        class MyObject(object):
+            _read_only_value = 1
+            _read_write_value = 1
+
+            @property
+            def read_only_value(self):
+                "A read-only attribute"
+                return self._read_only_value
+
+            @property
+            def read_write_value(self):
+                return self._read_write_value
+
+            @read_write_value.setter
+            def read_write_value(self, value):
+                self._read_write_value = value
+
+
+
+        my_object = MyObject()
+
+        assert my_object.read_only_value == 1
+        assert MyObject.read_only_value.__doc__ == "A read-only attribute"
+
+        with pytest.raises(AttributeError):
+            my_object.read_only_value = 2
+        my_object.read_write_value = 2
+
+        depickled_obj = pickle_depickle(my_object)
+
+        assert depickled_obj.read_only_value == 1
+        assert depickled_obj.read_write_value == 2
+
+        # make sure the depickled read_only_value attribute is still read-only
+        with pytest.raises(AttributeError):
+            my_object.read_only_value = 2
+
+        # make sure the depickled read_write_value attribute is writeable
+        depickled_obj.read_write_value = 3
+        assert depickled_obj.read_write_value == 3
+        type(depickled_obj).read_only_value.__doc__ == "A read-only attribute"
+
+
     def test_namedtuple(self):
         MyTuple = collections.namedtuple('MyTuple', ['a', 'b', 'c'])
         t1 = MyTuple(1, 2, 3)
@@ -1533,7 +1726,16 @@
             # grown by more than a few MB as closures are garbage collected at
             # the end of each remote function call.
             growth = w.memsize() - reference_size
-            assert growth < 1e7, growth
+
+            # For some reason, the memory growth after processing 100MB of
+            # data is ~10MB on MacOS, and ~1MB on Linux, so the upper bound on
+            # memory growth we use is only tight for MacOS. However,
+            # - 10MB is still 10x lower than the expected memory growth in case
+            # of a leak (which would be the total size of the processed data,
+            # 100MB)
+            # - the memory usage growth does not increase if using 10000
+            # iterations instead of 100 as used now (100x more data)
+            assert growth < 1.5e7, growth
 
         """.format(protocol=self.protocol)
         assert_run_python_script(code)
@@ -1666,6 +1868,17 @@
                 with pytest.raises(AttributeError):
                     obj.non_registered_attribute = 1
 
+            class SubclassWithSlots(ClassWithSlots):
+                def __init__(self):
+                    self.unregistered_attribute = 1
+
+            obj = SubclassWithSlots()
+            s = cloudpickle.dumps(obj, protocol=self.protocol)
+            del SubclassWithSlots
+            depickled_obj = cloudpickle.loads(s)
+            assert depickled_obj.unregistered_attribute == 1
+
+
     @unittest.skipIf(not hasattr(types, "MappingProxyType"),
                      "Old versions of Python do not have this type.")
     def test_mappingproxy(self):
@@ -1876,6 +2089,22 @@
         with pytest.raises(pickle.PicklingError, match='recursion'):
             cloudpickle.dumps(a)
 
+    def test_out_of_band_buffers(self):
+        if self.protocol < 5:
+            pytest.skip("Need Pickle Protocol 5 or later")
+        np = pytest.importorskip("numpy")
+
+        class LocallyDefinedClass:
+            data = np.zeros(10)
+
+        data_instance = LocallyDefinedClass()
+        buffers = []
+        pickle_bytes = cloudpickle.dumps(data_instance, protocol=self.protocol,
+                                         buffer_callback=buffers.append)
+        assert len(buffers) == 1
+        reconstructed = pickle.loads(pickle_bytes, buffers=buffers)
+        np.testing.assert_allclose(reconstructed.data, data_instance.data)
+
 
 class Protocol2CloudPickleTest(CloudPickleTest):
 


Reply via email to