Skip to content

Commit

Permalink
Also emit a warning if the class decorated by @NoPickle directly impl…
Browse files Browse the repository at this point in the history
…ements one of the methods we override.
  • Loading branch information
jamadden committed Jul 21, 2018
1 parent a267e27 commit e11ad8a
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 10 deletions.
4 changes: 3 additions & 1 deletion CHANGES.rst
Expand Up @@ -11,7 +11,9 @@
subclasses of ``Persistent``, depending on the MRO,
but that's always been the case for regular objects). A
``Persistent`` subclass being decorated with ``@NoPickle`` doesn't
make much sense, so a ``RuntimeWarning`` is issued.
make much sense, so a ``RuntimeWarning`` is issued. A warning is
also issued if the class directly implements one of the pickle
protocol methods.

- Updating objects that use ``createFieldProperties`` or otherwise
have ``FieldProperty`` objects in their type is at least 10% faster
Expand Down
13 changes: 8 additions & 5 deletions src/nti/externalization/persistence.py
Expand Up @@ -18,6 +18,7 @@
from itertools import izip
except ImportError: # pragma: no cover
izip = zip
import warnings

import persistent
from persistent.list import PersistentList
Expand Down Expand Up @@ -277,14 +278,16 @@ class CanPickle(Persistent, Root):
def __reduce_ex__(self, protocol=0):
raise TypeError(msg)


cls.__reduce_ex__ = __reduce_ex__
cls.__reduce__ = __reduce_ex__
cls.__getstate__ = __reduce_ex__
for meth in '__reduce_ex__', '__reduce__', '__getstate__':
if vars(cls).get(meth) is not None:
warnings.warn(RuntimeWarning("Using @NoPickle an a class that implements " + meth),
stacklevel=2)
setattr(cls, meth, __reduce_ex__)

if issubclass(cls, persistent.Persistent):
import warnings
warnings.warn(RuntimeWarning("Using @NoPickle an a Persistent subclass"),
stacklevel=2)



return cls
32 changes: 28 additions & 4 deletions src/nti/externalization/tests/test_persistence.py
Expand Up @@ -286,13 +286,37 @@ def test_persistent_mixin2(self):
def test_persistent_mixin3(self):
self._all_persists_fail(GlobalNoPicklePersistentMixin3)

def test_persistent_emits_warning(self):
def _check_emits_warning(self, kind):
with warnings.catch_warnings(record=True) as w:
class P(Persistent):
pass
NoPickle(P)
NoPickle(kind)

assert_that(w, has_length(1))
assert_that(w[0].message, is_(RuntimeWarning))
self.assertIn("Using @NoPickle",
str(w[0].message))

def test_persistent_emits_warning(self):
class P(Persistent):
pass
self._check_emits_warning(P)

def test_getstate_emits_warning(self):
class P(object):
def __getstate__(self):
"Does nothing"

self._check_emits_warning(P)

def test_reduce_emits_warning(self):
class P(object):
def __reduce__(self):
"Does nothing"

self._check_emits_warning(P)

def test_reduce_ex_emits_warning(self):
class P(object):
def __reduce_ex__(self):
"Does nothing"

self._check_emits_warning(P)

0 comments on commit e11ad8a

Please sign in to comment.