Debian

Available patches from Ubuntu

To see Ubuntu differences wrt. to Debian, write down a grep-dctrl query identifying the packages you're interested in:
grep-dctrl -n -sPackage Sources.Debian
(e.g. -FPackage linux-ntfs or linux-ntfs)

Modified packages are listed below:

Debian ( Changelog | PTS | Bugs ) Ubuntu ( Changelog | txt | LP | Bugs ) | Diff from Ubuntu

Source: nose2

nose2 (0.9.1-3ubuntu3) focal; urgency=medium * Drop Python2 package. There are no remaining changes compared to the Debian packaging. -- Jeremy Bicha <jbicha@ubuntu.com> Sun, 05 Apr 2020 08:21:55 -0400 nose2 (0.9.1-3ubuntu2) focal; urgency=medium * Build the python2 module again, needed for the transition. -- Matthias Klose <doko@ubuntu.com> Thu, 16 Jan 2020 22:25:12 +0100 nose2 (0.9.1-3build1) focal; urgency=medium * No-change rebuild to generate dependencies on python2. -- Matthias Klose <doko@ubuntu.com> Tue, 17 Dec 2019 12:52:24 +0000

Modifications :
  1. There are no changes

Debian ( Changelog | PTS | Bugs ) Ubuntu ( Changelog | txt | LP | Bugs ) | Diff from Ubuntu

Source: python-cachecontrol

python-cachecontrol (0.12.6-1ubuntu2) focal; urgency=medium * Drop the python2 package -- Jeremy Bicha <jbicha@ubuntu.com> Thu, 02 Apr 2020 20:25:20 -0400 python-cachecontrol (0.12.6-1ubuntu1) focal; urgency=low * Merge from Debian unstable. Remaining changes: - Restore the python2 package, still needed by pydoctor. - Add dependency on lockfile. -- Gianfranco Costamagna <locutusofborg@debian.org> Wed, 19 Feb 2020 21:19:19 +0100

Modifications :
  1. Download patch debian/control

    --- 0.12.6-1/debian/control 2020-01-25 07:45:32.000000000 +0000 +++ 0.12.6-1ubuntu2/debian/control 2020-04-03 00:25:20.000000000 +0000 @@ -18,7 +18,7 @@ Testsuite: autopkgtest-pkg-python Package: python3-cachecontrol Architecture: all -Depends: python3-requests, ${misc:Depends}, ${python3:Depends} +Depends: python3-requests, ${misc:Depends}, ${python3:Depends}, python3-lockfile Description: caching algorithms in httplib2 for use with requests CacheControl is a port of the caching algorithms in httplib2 for use with requests session object.

Debian ( Changelog | PTS | Bugs ) Ubuntu ( Changelog | txt | LP | Bugs ) | Diff from Ubuntu

Source: python-persistent

python-persistent (4.4.3-0ubuntu4) focal; urgency=medium * Stop building the Python2 module. -- Matthias Klose <doko@ubuntu.com> Thu, 20 Feb 2020 13:59:31 +0100 python-persistent (4.4.3-0ubuntu3) focal; urgency=medium * No-change rebuild to drop python3.7. -- Matthias Klose <doko@ubuntu.com> Tue, 18 Feb 2020 11:33:42 +0100 python-persistent (4.4.3-0ubuntu2) focal; urgency=medium * debian/tests/control: - use python2 instead of python -- Sebastien Bacher <seb128@ubuntu.com> Fri, 13 Dec 2019 22:07:49 +0100 python-persistent (4.4.3-0ubuntu1) focal; urgency=medium * New upstream version. -- Matthias Klose <doko@ubuntu.com> Mon, 21 Oct 2019 08:53:27 +0200 python-persistent (4.2.2-2build1) focal; urgency=medium * No-change rebuild to build with python3.8. -- Matthias Klose <doko@ubuntu.com> Fri, 18 Oct 2019 18:27:52 +0000

Modifications :
  1. Download patch persistent/timestamp.py

    --- 4.2.2-3/persistent/timestamp.py 2016-11-29 16:53:26.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/timestamp.py 2018-10-22 12:41:54.000000000 +0000 @@ -18,23 +18,29 @@ import math import struct import sys +from persistent._compat import PURE_PYTHON + _RAWTYPE = bytes _MAXINT = sys.maxsize -def _makeOctets(s): - if sys.version_info < (3,): - return bytes(s) - return bytes(s, 'ascii') #pragma NO COVERAGE - - -_ZERO = _makeOctets('\x00' * 8) - +_ZERO = b'\x00' * 8 -def _wraparound(x): +try: # Make sure to overflow and wraparound just # like the C code does. - return int(((x + (_MAXINT + 1)) & ((_MAXINT << 1) + 1)) - (_MAXINT + 1)) - + from ctypes import c_long +except ImportError: # pragma: no cover + # XXX: This is broken on 64-bit windows, where + # sizeof(long) != sizeof(Py_ssize_t) + # sizeof(long) == 4, sizeof(Py_ssize_t) == 8 + # It can be fixed by setting _MAXINT = 2 ** 31 - 1 on all + # win32 platforms, but then that breaks PyPy3 64 bit for an unknown + # reason. + def _wraparound(x): + return int(((x + (_MAXINT + 1)) & ((_MAXINT << 1) + 1)) - (_MAXINT + 1)) +else: + def _wraparound(x): + return c_long(x).value class _UTC(datetime.tzinfo): def tzname(self): @@ -47,6 +53,7 @@ class _UTC(datetime.tzinfo): return dt def _makeUTC(y, mo, d, h, mi, s): + s = round(s, 6) # microsecond precision, to match the C implementation usec, sec = math.modf(s) sec = int(sec) usec = int(usec * 1e6) @@ -54,12 +61,12 @@ def _makeUTC(y, mo, d, h, mi, s): _EPOCH = _makeUTC(1970, 1, 1, 0, 0, 0) -_SCONV = 60.0 / (1<<16) / (1<<16) +_TS_SECOND_BYTES_BIAS = 60.0 / (1<<16) / (1<<16) def _makeRaw(year, month, day, hour, minute, second): a = (((year - 1900) * 12 + month - 1) * 31 + day - 1) a = (a * 24 + hour) * 60 + minute - b = int(second / _SCONV) # Don't round() this; the C version does simple truncation + b = int(second / _TS_SECOND_BYTES_BIAS) # Don't round() this; the C version does simple truncation return struct.pack('>II', a, b) def _parseRaw(octets): @@ -69,7 +76,7 @@ def _parseRaw(octets): day = a // (60 * 24) % 31 + 1 month = a // (60 * 24 * 31) % 12 + 1 year = a // (60 * 24 * 31 * 12) + 1900 - second = round(b * _SCONV, 6) #microsecond precision + second = b * _TS_SECOND_BYTES_BIAS return (year, month, day, hour, minute, second) @@ -77,6 +84,7 @@ class pyTimeStamp(object): __slots__ = ('_raw', '_elements') def __init__(self, *args): + self._elements = None if len(args) == 1: raw = args[0] if not isinstance(raw, _RAWTYPE): @@ -84,14 +92,18 @@ class pyTimeStamp(object): if len(raw) != 8: raise TypeError('Raw must be 8 octets') self._raw = raw - self._elements = _parseRaw(raw) elif len(args) == 6: self._raw = _makeRaw(*args) - self._elements = args + # Note that we don't preserve the incoming arguments in self._elements, + # we derive them from the raw value. This is because the incoming + # seconds value could have more precision than would survive + # in the raw data, so we must be consistent. else: raise TypeError('Pass either a single 8-octet arg ' 'or 5 integers and a float') + self._elements = _parseRaw(self._raw) + def raw(self): return self._raw @@ -165,7 +177,7 @@ class pyTimeStamp(object): x = _wraparound(x) - if x == -1: #pragma: no cover + if x == -1: # pragma: no cover # The C version has this condition, but it's not clear # why; it's also not immediately obvious what bytestring # would generate this---hence the no-cover @@ -201,6 +213,8 @@ class pyTimeStamp(object): try: - from persistent._timestamp import TimeStamp -except ImportError: #pragma NO COVER - TimeStamp = pyTimeStamp + from persistent._timestamp import TimeStamp as CTimeStamp +except ImportError: # pragma: no cover + CTimeStamp = None + +TimeStamp = pyTimeStamp if PURE_PYTHON or CTimeStamp is None else CTimeStamp
  2. Download patch persistent/ring.py

    --- 4.2.2-3/persistent/ring.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/ring.py 2018-10-22 12:41:54.000000000 +0000 @@ -144,21 +144,12 @@ class _DequeRing(object): try: - from cffi import FFI + from persistent import _ring except ImportError: # pragma: no cover _CFFIRing = None else: - - import os - this_dir = os.path.dirname(os.path.abspath(__file__)) - - ffi = FFI() - with open(os.path.join(this_dir, 'ring.h')) as f: - ffi.cdef(f.read()) - - _FFI_RING = ffi.verify(""" - #include "ring.c" - """, include_dirs=[this_dir]) + ffi = _ring.ffi + _FFI_RING = _ring.lib _OGA = object.__getattribute__ _OSA = object.__setattr__
  3. Download patch persistent.egg-info/entry_points.txt

    --- 4.2.2-3/persistent.egg-info/entry_points.txt 2016-11-29 19:07:54.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent.egg-info/entry_points.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ - \ No newline at end of file
  4. Download patch persistent/_compat.py

    --- 4.2.2-3/persistent/_compat.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/_compat.py 2018-10-22 12:41:54.000000000 +0000 @@ -13,44 +13,24 @@ ############################################################################## import sys +import os -if sys.version_info[0] > 2: #pragma NO COVER +PURE_PYTHON = os.environ.get('PURE_PYTHON') + +if sys.version_info[0] > 2: import copyreg as copy_reg from collections import UserDict as IterableUserDict from collections import UserList from sys import intern - def _u(s): - return s - - def _b(s): - if isinstance(s, str): - return s.encode('unicode_escape') - return s - - def _native(s): - if isinstance(s, bytes): - return s.decode('unicode_escape') - return s - PYTHON3 = True PYTHON2 = False -else: #pragma NO COVER +else: # pragma: no cover import copy_reg from UserDict import IterableUserDict from UserList import UserList - def _u(s): - return unicode(s, 'unicode_escape') - - def _native(s): - if isinstance(s, unicode): - return s.encode('unicode_escape') - return s - - _b = _native - PYTHON3 = False PYTHON2 = True
  5. Download patch PKG-INFO
  6. Download patch README.rst

    --- 4.2.2-3/README.rst 2016-05-05 12:39:16.000000000 +0000 +++ 4.4.3-0ubuntu4/README.rst 2018-10-22 12:41:54.000000000 +0000 @@ -1,19 +1,22 @@ ``persistent``: automatic persistence for Python objects ========================================================= -.. image:: https://travis-ci.org/zopefoundation/persistent.png?branch=master +.. image:: https://travis-ci.org/zopefoundation/persistent.svg?branch=master :target: https://travis-ci.org/zopefoundation/persistent +.. image:: https://coveralls.io/repos/github/zopefoundation/persistent/badge.svg?branch=master + :target: https://coveralls.io/github/zopefoundation/persistent?branch=master + .. image:: https://readthedocs.org/projects/persistent/badge/?version=latest :target: http://persistent.readthedocs.org/en/latest/ :alt: Documentation Status .. image:: https://img.shields.io/pypi/v/persistent.svg - :target: https://pypi.python.org/pypi/persistent - :alt: PyPI + :target: https://pypi.org/project/persistent + :alt: Latest release .. image:: https://img.shields.io/pypi/pyversions/persistent.svg - :target: https://pypi.python.org/pypi/persistent + :target: https://pypi.org/project/persistent :alt: Python versions This package contains a generic persistence implementation for Python. It @@ -21,7 +24,9 @@ forms the core protocol for making objec a database such as the ZODB. Please see the Sphinx documentation (``docs/index.rst``) for further -information. +information, or view the documentation at Read The Docs, for either +the latest (``http://persistent.readthedocs.io/en/latest/``) or stable +release (``http://persistent.readthedocs.io/en/stable/``). .. note::
  7. Download patch docs/api/cache.rst

    --- 4.2.2-3/docs/api/cache.rst 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/docs/api/cache.rst 2018-10-22 12:41:54.000000000 +0000 @@ -47,13 +47,13 @@ Caches have a :meth:`new_ghost` method t >>> jar = ResettingJar() >>> cache = persistent.PickleCache(jar, 10, 100) >>> ob = C.__new__(C) - >>> cache.new_ghost('1', ob) + >>> cache.new_ghost(b'1', ob) >>> ob._p_changed >>> ob._p_jar is jar True - >>> ob._p_oid - '1' + >>> ob._p_oid == b'1' + True >>> cache.cache_non_ghost_count 0
  8. Download patch persistent/tests/utils.py

    --- 4.2.2-3/persistent/tests/utils.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/tests/utils.py 2018-10-22 12:41:54.000000000 +0000 @@ -18,19 +18,9 @@ class ResettingJar(object): obj._p_jar = self self.cache[obj._p_oid] = obj - def close(self): - pass # the following methods must be implemented to be a jar - def setklassstate(self): - # I don't know what this method does, but the pickle cache - # constructor calls it. - pass - - def register(self, obj): - self.registered[obj] = 1 - def setstate(self, obj): # Trivial setstate() implementation that just re-initializes # the object. This isn't what setstate() is supposed to do, @@ -56,8 +46,6 @@ class RememberingJar(object): self.obj = obj self.remembered = obj.__getstate__() - def close(self): - pass def fake_commit(self): self.remembered = self.obj.__getstate__() @@ -65,11 +53,6 @@ class RememberingJar(object): # the following methods must be implemented to be a jar - def setklassstate(self): - # I don't know what this method does, but the pickle cache - # constructor calls it. - pass - def register(self, obj): self.registered[obj] = 1 @@ -79,4 +62,3 @@ class RememberingJar(object): # This isn't what setstate() is supposed to do, # but it suffices for the tests. obj.__setstate__(self.remembered) -
  9. Download patch persistent/tests/test_wref.py

    --- 4.2.2-3/persistent/tests/test_wref.py 2016-05-05 12:39:16.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/tests/test_wref.py 2018-10-22 12:41:54.000000000 +0000 @@ -24,21 +24,19 @@ class WeakRefTests(unittest.TestCase): return self._getTargetClass()(ob) def test_ctor_target_wo_jar(self): - from persistent._compat import _b target = _makeTarget() wref = self._makeOne(target) self.assertTrue(wref._v_ob is target) - self.assertEqual(wref.oid, _b('OID')) + self.assertEqual(wref.oid, b'OID') self.assertTrue(wref.dm is None) self.assertFalse('database_name' in wref.__dict__) def test_ctor_target_w_jar(self): - from persistent._compat import _b target = _makeTarget() target._p_jar = jar = _makeJar() wref = self._makeOne(target) self.assertTrue(wref._v_ob is target) - self.assertEqual(wref.oid, _b('OID')) + self.assertEqual(wref.oid, b'OID') self.assertTrue(wref.dm is jar) self.assertEqual(wref.database_name, 'testing') @@ -181,14 +179,13 @@ class PersistentWeakKeyDictionaryTests(u def test___setstate___empty(self): from persistent.wref import WeakRef - from persistent._compat import _b jar = _makeJar() - KEY = _b('KEY') - KEY2 = _b('KEY2') - KEY3 = _b('KEY3') - VALUE = _b('VALUE') - VALUE2 = _b('VALUE2') - VALUE3 = _b('VALUE3') + KEY = b'KEY' + KEY2 = b'KEY2' + KEY3 = b'KEY3' + VALUE = b'VALUE' + VALUE2 = b'VALUE2' + VALUE3 = b'VALUE3' key = jar[KEY] = _makeTarget(oid=KEY) key._p_jar = jar kref = WeakRef(key) @@ -208,7 +205,7 @@ class PersistentWeakKeyDictionaryTests(u value3._p_jar = jar pwkd = self._makeOne(None) pwkd.__setstate__({'data': - [(kref, value), (kref2, value2), (kref3, value3)]}) + [(kref, value), (kref2, value2), (kref3, value3)]}) self.assertTrue(pwkd[key] is value) self.assertTrue(pwkd.get(key2) is None) self.assertTrue(pwkd[key3] is value3) @@ -315,22 +312,19 @@ class PersistentWeakKeyDictionaryTests(u target = self._makeOne(None) target.update(source) self.assertTrue(target[key] is value) - -def _makeTarget(oid='OID', **kw): + +def _makeTarget(oid=b'OID'): from persistent import Persistent - from persistent._compat import _b class Derived(Persistent): def __hash__(self): return hash(self._p_oid) def __eq__(self, other): return self._p_oid == other._p_oid - def __repr__(self): + def __repr__(self): # pragma: no cover return 'Derived: %s' % self._p_oid derived = Derived() - for k, v in kw.items(): - setattr(derived, k, v) - derived._p_oid = _b(oid) + derived._p_oid = oid return derived def _makeJar(): @@ -341,7 +335,4 @@ def _makeJar(): return _Jar() def test_suite(): - return unittest.TestSuite(( - unittest.makeSuite(WeakRefTests), - unittest.makeSuite(PersistentWeakKeyDictionaryTests), - )) + return unittest.defaultTestLoader.loadTestsFromName(__name__)
  10. Download patch debian/rules

    --- 4.2.2-3/debian/rules 2019-09-26 20:07:10.000000000 +0000 +++ 4.4.3-0ubuntu4/debian/rules 2020-02-20 12:59:10.000000000 +0000 @@ -15,7 +15,7 @@ override_dh_auto_build: PYTHONPATH=. \ http_proxy=http://127.0.0.1:9 \ https_proxy=https://127.0.0.1:9 \ - python3 -m sphinx -qNE -b html docs build/html + sphinx-build -qNE -b html docs build/html override_dh_auto_clean: dh_auto_clean
  11. Download patch persistent.egg-info/not-zip-safe

    --- 4.2.2-3/persistent.egg-info/not-zip-safe 2016-05-05 12:43:40.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent.egg-info/not-zip-safe 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -
  12. Download patch persistent/list.py

    --- 4.2.2-3/persistent/list.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/list.py 2018-10-22 12:41:54.000000000 +0000 @@ -21,11 +21,13 @@ from persistent._compat import UserList from persistent._compat import PYTHON2 class PersistentList(UserList, persistent.Persistent): + """A persistent wrapper for list objects. + + Mutating instances of this class will cause them to be marked + as changed and automatically persisted. + """ __super_setitem = UserList.__setitem__ __super_delitem = UserList.__delitem__ - if PYTHON2: # pragma: no cover - __super_setslice = UserList.__setslice__ - __super_delslice = UserList.__delslice__ __super_iadd = UserList.__iadd__ __super_imul = UserList.__imul__ __super_append = UserList.append @@ -44,13 +46,17 @@ class PersistentList(UserList, persisten self.__super_delitem(i) self._p_changed = 1 - def __setslice__(self, i, j, other): - self.__super_setslice(i, j, other) - self._p_changed = 1 + if PYTHON2: # pragma: no cover + __super_setslice = UserList.__setslice__ + __super_delslice = UserList.__delslice__ - def __delslice__(self, i, j): - self.__super_delslice(i, j) - self._p_changed = 1 + def __setslice__(self, i, j, other): + self.__super_setslice(i, j, other) + self._p_changed = 1 + + def __delslice__(self, i, j): + self.__super_delslice(i, j) + self._p_changed = 1 def __iadd__(self, other): L = self.__super_iadd(other) @@ -90,9 +96,3 @@ class PersistentList(UserList, persisten def extend(self, other): self.__super_extend(other) self._p_changed = 1 - - # This works around a bug in Python 2.1.x (up to 2.1.2 at least) where the - # __cmp__ bogusly raises a RuntimeError, and because this is an extension - # class, none of the rich comparison stuff works anyway. - def __cmp__(self, other): - return cmp(self.data, self._UserList__cast(other))
  13. Download patch MANIFEST.in

    --- 4.2.2-3/MANIFEST.in 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/MANIFEST.in 2018-10-22 12:41:54.000000000 +0000 @@ -1,5 +1,9 @@ +prune terryfy + include *.txt include *.rst +include *.sh +include *.yml recursive-include docs * recursive-include persistent * @@ -13,6 +17,7 @@ global-exclude coverage.xml prune docs/_build prune persistent/__pycache__ + include .coveragerc include .travis.yml include buildout.cfg
  14. Download patch persistent/interfaces.py

    --- 4.2.2-3/persistent/interfaces.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/interfaces.py 2018-10-22 12:41:54.000000000 +0000 @@ -23,7 +23,7 @@ try: from persistent.cPersistence import UPTODATE from persistent.cPersistence import CHANGED from persistent.cPersistence import STICKY -except ImportError: #pragma NO COVER +except ImportError: # pragma: no cover GHOST = -1 UPTODATE = 0 CHANGED = 1 @@ -166,6 +166,14 @@ class IPersistent(Interface): these objects are invalidated, they immediately reload their state from their data manager, and are then in the saved state. + reprs + + By default, persistent objects include the reprs of their + _p_oid and _p_jar, if any, in their repr. If a subclass implements + the optional method ``_p_repr``, it will be called and its results returned + instead of the default repr; if this method raises an exception, that + exception will be caught and its repr included in the default repr. + """ _p_jar = Attribute( @@ -314,10 +322,10 @@ class IPersistent(Interface): def _p_getattr(name): """Test whether the base class must handle the name - + The method unghostifies the object, if necessary. The method records the object access, if necessary. - + This method should be called by subclass __getattribute__ implementations before doing anything else. If the method returns True, then __getattribute__ implementations must delegate @@ -471,7 +479,7 @@ class IPickleCache(Interface): """ Perform an incremental garbage collection sweep. o Reduce number of non-ghosts to 'cache_size', if possible. - + o Ghostify in LRU order. o Skip dirty or sticky objects. @@ -505,7 +513,7 @@ class IPickleCache(Interface): If the object's '_p_jar' is not None, raise. - If 'oid' is already in the cache, raise. + If 'oid' is already in the cache, raise. """ def reify(to_reify): @@ -536,7 +544,7 @@ class IPickleCache(Interface): o Any OID corresponding to a p-class will cause the corresponding p-class to be removed from the cache. - o For all other OIDs, ghostify the corrsponding object and + o For all other OIDs, ghostify the corrsponding object and remove it from the ring. """
  15. Download patch docs/api/collections.rst

    --- 4.2.2-3/docs/api/collections.rst 1970-01-01 00:00:00.000000000 +0000 +++ 4.4.3-0ubuntu4/docs/api/collections.rst 2018-10-22 12:41:54.000000000 +0000 @@ -0,0 +1,14 @@ +======================== + Persistent Collections +======================== + +The ``persistent`` package provides two simple collections that are +persistent and keep track of when they are mutated in place. + +.. autoclass:: persistent.mapping.PersistentMapping + :members: + :show-inheritance: + +.. autoclass:: persistent.list.PersistentList + :members: + :show-inheritance:
  16. Download patch persistent.egg-info/PKG-INFO
  17. Download patch debian/compat

    --- 4.2.2-3/debian/compat 1970-01-01 00:00:00.000000000 +0000 +++ 4.4.3-0ubuntu4/debian/compat 2019-02-23 18:10:46.000000000 +0000 @@ -0,0 +1 @@ +10
  18. Download patch persistent.egg-info/dependency_links.txt

    --- 4.2.2-3/persistent.egg-info/dependency_links.txt 2016-11-29 19:07:54.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent.egg-info/dependency_links.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -
  19. Download patch persistent/tests/attrhooks.py

    --- 4.2.2-3/persistent/tests/attrhooks.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/tests/attrhooks.py 2018-10-22 12:41:54.000000000 +0000 @@ -35,9 +35,8 @@ class OverridesGetattr(Persistent): """ # Don't pretend we have any special attributes. if name.startswith("__") and name.endswrith("__"): - raise AttributeError(name) - else: - return name.upper(), self._p_changed + raise AttributeError(name) # pragma: no cover + return name.upper(), self._p_changed class VeryPrivate(Persistent):
  20. Download patch debian/control

    --- 4.2.2-3/debian/control 2019-09-26 20:07:10.000000000 +0000 +++ 4.4.3-0ubuntu4/debian/control 2020-02-20 12:58:54.000000000 +0000 @@ -3,12 +3,12 @@ Maintainer: Debian Python Modules Team < Uploaders: Barry Warsaw <barry@debian.org> Section: python Priority: optional -Build-Depends: debhelper-compat (= 10), +Build-Depends: debhelper (>= 10), dh-python, - python3-all-dev, python3-repoze.sphinx.autointerface, + python3-all-dev, python3-setuptools, - python3-sphinx, + python3-manuel, python3-zope.interface Standards-Version: 3.9.8 Homepage: https://pypi.python.org/pypi/persistent/
  21. Download patch .travis.yml

    --- 4.2.2-3/.travis.yml 2016-11-29 16:53:26.000000000 +0000 +++ 4.4.3-0ubuntu4/.travis.yml 2018-10-22 12:41:54.000000000 +0000 @@ -5,40 +5,76 @@ matrix: - os: linux python: 2.7 - os: linux - python: 3.3 - - os: linux python: 3.4 - os: linux python: 3.5 - os: linux + python: 3.6 + # Test for https://github.com/zopefoundation/persistent/issues/86 + env: CFLAGS="-fno-wrapv" + - os: linux python: pypy - os: linux python: pypy3 + - os: linux + dist: xenial + sudo: true + python: 3.7 + # It's important to use 'macpython' builds to get the least + # restrictive wheel tag. It's also important to avoid + # 'homebrew 3' because it floats instead of being a specific version. - os: osx language: generic - env: TERRYFY_PYTHON='homebrew 2' + env: TERRYFY_PYTHON='macpython 2.7' - os: osx language: generic env: TERRYFY_PYTHON='macpython 3.4' - os: osx language: generic - env: TERRYFY_PYTHON='homebrew 3.5' + env: TERRYFY_PYTHON='macpython 3.5' + - os: osx + language: generic + env: TERRYFY_PYTHON='macpython 3.6.0' + - os: osx + language: generic + env: TERRYFY_PYTHON='macpython 3.7.0' + - services: + - docker + env: DOCKER_IMAGE=quay.io/pypa/manylinux1_x86_64 + before_install: + - if [[ $TRAVIS_TAG ]]; then bash .manylinux.sh; fi + - exit 0 + - services: + - docker + env: + - DOCKER_IMAGE=quay.io/pypa/manylinux1_i686 + - PRE_CMD=linux32 + before_install: + - if [[ $TRAVIS_TAG ]]; then bash .manylinux.sh; fi + - exit 0 + before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then git clone https://github.com/MacPython/terryfy; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then source terryfy/travis_tools.sh; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then get_python_environment $TERRYFY_PYTHON venv; fi - - if [[ "$TERRYFY_PYTHON" == "homebrew 3" ]]; then alias pip=`which pip3` ; fi install: - - pip install -e . + - pip install -U pip setuptools cffi wheel coverage coveralls + - pip install -U -e .[test] script: - - python setup.py -q test -q + - python --version + # make sure we can build a wheel + - python setup.py bdist_wheel + # coverage makes PyPy run about 3x slower, but the tests only take + # .4s to begin with (the whole process takes about 1.5), so that's + # still only 4.5s, which is maneagable. + - coverage run -m zope.testrunner --test-path=. --auto-color --auto-progress notifications: email: false after_success: + - coveralls - echo [distutils] > ~/.pypirc - echo index-servers = pypi >> ~/.pypirc - echo [pypi] >> ~/.pypirc - - echo repository=https://pypi.python.org/pypi >> ~/.pypirc - echo username=zope.wheelbuilder >> ~/.pypirc - echo password=$PYPIPASSWORD >> ~/.pypirc - if [[ $TRAVIS_TAG && "$TRAVIS_OS_NAME" == "osx" ]]; then pip install twine; fi @@ -47,4 +83,4 @@ after_success: env: global: - secure: "avoDyPgppusNzldim5fq35sk7HAn3B8zs2KgAe+8Yr3MDpFaxNV96rhNkTdHgsNNw4N+PVew1sFUxLY7HzACyywLFPUPT+YMRcPeqiuekkQoQxatYR0dEfHrxPpNyvXRxIV+nrKTEaIxQNyDcJcyRw6K7NSNxuu2NR6Dj+xJmKY=" + secure: "NTWzDr5p8KRPNt+sniTot7csbzC87rzir/XfLtENE0GpQ49FlKw3lBhsDqAPoD8Ea5lwiHXmC/C/ci1UZhFvVEkAoQ2qJlMRnhqUdRJSrqcitmRt0fT6mLaTd+Lr+DxKlBxpssobrEm2G42V/G1s0Ggym04OqF8T+s6MF5ywgJM="
  22. Download patch .coveragerc

    --- 4.2.2-3/.coveragerc 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/.coveragerc 2018-10-22 12:41:54.000000000 +0000 @@ -1,4 +1,12 @@ +[run] +source = persistent +omit = + persistent/_ring_build.py + [report] exclude_lines = - # pragma: no cover + pragma: no cover class I[A-Z]\w+\((Interface|I[A-Z].*)\): + raise NotImplementedError + raise AssertionError + if __name__ == '__main__':
  23. Download patch tox.ini

    --- 4.2.2-3/tox.ini 2016-05-05 12:39:16.000000000 +0000 +++ 4.4.3-0ubuntu4/tox.ini 2018-10-22 12:41:54.000000000 +0000 @@ -3,56 +3,40 @@ envlist = # Jython 2.7rc2 does work, but unfortunately has an issue running # with Tox 1.9.2 (http://bugs.jython.org/issue2325) # py27,py27-pure,pypy,py33,py34,pypy3,jython,coverage,docs - py27,py27-pure,py27-pure-cffi,pypy,py33,py34,py35,pypy3,coverage,docs + py27,py27-pure,py27-pure-cffi,pypy,py34,py35,py36,py37,pypy3,coverage,docs [testenv] deps = - zope.interface + cffi + .[test] commands = - python setup.py -q test -q - -[testenv:jython] -commands = - jython setup.py -q test -q + zope-testrunner --test-path=. [testenv:py27-pure] basepython = python2.7 setenv = PURE_PYTHON = 1 - PIP_CACHE_DIR = {envdir}/.cache -deps = - {[testenv]deps} -commands = - python setup.py -q test -q [testenv:py27-pure-cffi] basepython = python2.7 setenv = PURE_PYTHON = 1 - PIP_CACHE_DIR = {envdir}/.cache USING_CFFI = 1 -deps = - {[testenv]deps} - cffi -commands = - python setup.py -q test -q - [testenv:coverage] +usedevelop = true basepython = - python2.7 -setenv = - USING_CFFI = 1 + python3.6 commands = - nosetests --with-xunit --with-xcoverage + coverage run -m zope.testrunner --test-path=. + coverage report --fail-under=93 deps = - zope.interface - nose + {[testenv]deps} coverage - nosexcover - cffi +setenv = + USING_CFFI = 1 [testenv:docs] basepython = @@ -61,6 +45,4 @@ commands = sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest deps = - zope.interface - Sphinx - repoze.sphinx.autointerface + .[docs]
  24. Download patch persistent.egg-info/top_level.txt

    --- 4.2.2-3/persistent.egg-info/top_level.txt 2016-11-29 19:07:54.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent.egg-info/top_level.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -persistent
  25. Download patch persistent/tests/cucumbers.py

    --- 4.2.2-3/persistent/tests/cucumbers.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/tests/cucumbers.py 2018-10-22 12:41:54.000000000 +0000 @@ -14,29 +14,22 @@ # Example objects for pickling. from persistent import Persistent -from persistent._compat import PYTHON2 - def print_dict(d): - d = d.items() - d.sort() + d = sorted(d.items()) print('{%s}' % (', '.join( [('%r: %r' % (k, v)) for (k, v) in d] ))) def cmpattrs(self, other, *attrs): + result = 0 for attr in attrs: if attr[:3] in ('_v_', '_p_'): - continue - lhs, rhs = getattr(self, attr, None), getattr(other, attr, None) - if PYTHON2: - c = cmp(lhs, rhs) - if c: - return c - else: - if lhs != rhs: - return 1 - return 0 + raise AssertionError("_v_ and _p_ attrs not allowed") + lhs = getattr(self, attr, None) + rhs = getattr(other, attr, None) + result += lhs != rhs + return result class Simple(Persistent): def __init__(self, name, **kw): @@ -84,7 +77,7 @@ class Slotted(Persistent): @property def _attrs(self): - return list(self.__dict__.keys()) + raise NotImplementedError() def __eq__(self, other): return cmpattrs(self, other, '__class__', *self._attrs) == 0
  26. Download patch docs/api/attributes.rst

    --- 4.2.2-3/docs/api/attributes.rst 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/docs/api/attributes.rst 2018-10-22 12:41:54.000000000 +0000 @@ -309,3 +309,21 @@ object as changed if the name starts wit Traceback (most recent call last): ... AttributeError: tmp_z + +If we attempt to delete ``_p_oid``, we find that we can't, and the +object is also not activated or changed: + +.. doctest:: + + >>> del o._p_oid + Traceback (most recent call last): + ... + ValueError: can't delete _p_oid of cached object + >>> o._p_changed + False + +We are allowed to delete ``_p_changed``, which sets it to ``None``: + + >>> del o._p_changed + >>> o._p_changed is None + True
  27. Download patch docs/conf.py

    --- 4.2.2-3/docs/conf.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/docs/conf.py 2018-10-22 12:41:54.000000000 +0000 @@ -50,16 +50,16 @@ master_doc = 'index' # General information about the project. project = u'persistent' -copyright = u'2011, ZODB Developers <zope-dev@zope.org>' +copyright = u'2011,2016 ZODB Developers <zope-dev@zope.org>' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '3.10' +version = '4.2' # The full version, including alpha/beta/rc tags. -release = '3.10b1' +release = '4.2.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -266,4 +266,4 @@ epub_copyright = u'2011, ZODB Developers # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} +intersphinx_mapping = {'https://docs.python.org/': None}
  28. Download patch persistent/_timestamp.c

    --- 4.2.2-3/persistent/_timestamp.c 2016-11-29 16:53:26.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/_timestamp.c 2018-10-22 12:41:54.000000000 +0000 @@ -27,9 +27,75 @@ static char TimeStampModule_doc[] = "$Id$\n"; +/* A magic constant having the value 0.000000013969839. When an + number of seconds between 0 and 59 is *divided* by this number, we get + a number between 0 (for 0), 71582786 (for 1) and 4223384393 (for 59), + all of which can be represented in a 32-bit unsigned integer, suitable + for packing into 4 bytes using `TS_PACK_UINT32_INTO_BYTES`. + To get (close to) the original seconds back, use + `TS_UNPACK_UINT32_FROM_BYTES` and *multiply* by this number. + */ +#define TS_SECOND_BYTES_BIAS ((double)((double)60) / ((double)(0x10000)) / ((double)(0x10000))) +#define TS_BASE_YEAR 1900 +#define TS_MINUTES_PER_DAY 1440 +/* We pretend there are always 31 days in a month; this has us using + 372 days in a year in some calculations */ +#define TS_DAYS_PER_MONTH 31 +#define TS_MONTHS_PER_YEAR 12 +#define TS_MINUTES_PER_MONTH (TS_DAYS_PER_MONTH * TS_MINUTES_PER_DAY) +#define TS_MINUTES_PER_YEAR (TS_MINUTES_PER_MONTH * TS_MONTHS_PER_YEAR) + +/* The U suffixes matter on these constants to be sure + the compiler generates the appropriate instructions when + optimizations are enabled. On x86_64 GCC, if -fno-wrapv is given + and -O is used, the compiler might choose to treat these as 32 bit + signed quantities otherwise, producing incorrect results on + some corner cases. See + https://github.com/zopefoundation/persistent/issues/86 +*/ + +/** + * Given an unsigned int *v*, pack it into the four + * unsigned char bytes beginning at *bytes*. If *v* is larger + * than 2^31 (i.e., it doesn't fit in 32 bits), the results will + * be invalid (the first byte will be 0.) + * + * The inverse is `TS_UNPACK_UINT32_FROM_BYTES`. This is a + * lossy operation and may lose some lower-order precision. + * + */ +#define TS_PACK_UINT32_INTO_BYTES(v, bytes) do { \ + *(bytes) = v / 0x1000000U; \ + *(bytes + 1) = (v % 0x1000000U) / 0x10000U; \ + *(bytes + 2) = (v % 0x10000U) / 0x100U; \ + *(bytes + 3) = v % 0x100U; \ +} while (0) + +/** + * Given a sequence of four unsigned chars beginning at *bytes* + * as produced by `TS_PACK_UINT32_INTO_BYTES`, return the + * original unsigned int. + * + * Remember this is a lossy operation, and the value you get back + * may not exactly match the original value. If the original value + * was greater than 2^31 it will definitely not match. + */ +#define TS_UNPACK_UINT32_FROM_BYTES(bytes) (*(bytes) * 0x1000000U + *(bytes + 1) * 0x10000U + *(bytes + 2) * 0x100U + *(bytes + 3)) + typedef struct { PyObject_HEAD + /* + The first four bytes of data store the year, month, day, hour, and + minute as the number of minutes since Jan 1 00:00. + + The final four bytes store the seconds since 00:00 as + the number of microseconds. + + Both are normalized into those four bytes the same way with + TS_[UN]PACK_UINT32_INTO|FROM_BYTES. + */ + unsigned char data[8]; } TimeStamp; @@ -49,8 +115,6 @@ static short joff[2][12] = static double gmoff=0; -/* TODO: May be better (faster) to store in a file static. */ -#define SCONV ((double)60) / ((double)(1<<16)) / ((double)(1<<16)) static int leap(int year) @@ -69,7 +133,7 @@ TimeStamp_yad(int y) { double d, s; - y -= 1900; + y -= TS_BASE_YEAR; d = (y - 1) * 365; if (y > 0) { @@ -101,7 +165,7 @@ TimeStamp_init_gmoff(void) return -1; } - gmoff = TimeStamp_abst(t->tm_year+1900, t->tm_mon, t->tm_mday - 1, + gmoff = TimeStamp_abst(t->tm_year + TS_BASE_YEAR, t->tm_mon, t->tm_mday - 1, t->tm_hour * 60 + t->tm_min, t->tm_sec); return 0; @@ -180,17 +244,17 @@ typedef struct int mi; } TimeStampParts; + static void TimeStamp_unpack(TimeStamp *self, TimeStampParts *p) { - unsigned long v; + unsigned int minutes_since_base; - v = (self->data[0] * 16777216 + self->data[1] * 65536 - + self->data[2] * 256 + self->data[3]); - p->y = v / 535680 + 1900; - p->m = (v % 535680) / 44640 + 1; - p->d = (v % 44640) / 1440 + 1; - p->mi = v % 1440; + minutes_since_base = TS_UNPACK_UINT32_FROM_BYTES(self->data); + p->y = minutes_since_base / TS_MINUTES_PER_YEAR + TS_BASE_YEAR; + p->m = (minutes_since_base % TS_MINUTES_PER_YEAR) / TS_MINUTES_PER_MONTH + 1; + p->d = (minutes_since_base % TS_MINUTES_PER_MONTH) / TS_MINUTES_PER_DAY + 1; + p->mi = minutes_since_base % TS_MINUTES_PER_DAY; } static double @@ -198,9 +262,8 @@ TimeStamp_sec(TimeStamp *self) { unsigned int v; - v = (self->data[4] * 16777216 + self->data[5] * 65536 - + self->data[6] * 256 + self->data[7]); - return SCONV * v; + v = TS_UNPACK_UINT32_FROM_BYTES(self->data +4); + return TS_SECOND_BYTES_BIAS * v; } static PyObject * @@ -423,13 +486,19 @@ PyObject * TimeStamp_FromDate(int year, int month, int day, int hour, int min, double sec) { + TimeStamp *ts = NULL; int d; + unsigned int years_since_base; + unsigned int months_since_base; + unsigned int days_since_base; + unsigned int hours_since_base; + unsigned int minutes_since_base; unsigned int v; - if (year < 1900) + if (year < TS_BASE_YEAR) return PyErr_Format(PyExc_ValueError, - "year must be greater than 1900: %d", year); + "year must be greater than %d: %d", TS_BASE_YEAR, year); CHECK_RANGE(month, 1, 12); d = days_in_month(year, month - 1); if (day < 1 || day > d) @@ -444,19 +513,19 @@ TimeStamp_FromDate(int year, int month, "second must be between 0 and 59: %f", sec); */ ts = (TimeStamp *)PyObject_New(TimeStamp, &TimeStamp_type); - v = (((year - 1900) * 12 + month - 1) * 31 + day - 1); - v = (v * 24 + hour) * 60 + min; - ts->data[0] = v / 16777216; - ts->data[1] = (v % 16777216) / 65536; - ts->data[2] = (v % 65536) / 256; - ts->data[3] = v % 256; - sec /= SCONV; - v = (unsigned int)sec; - ts->data[4] = v / 16777216; - ts->data[5] = (v % 16777216) / 65536; - ts->data[6] = (v % 65536) / 256; - ts->data[7] = v % 256; + /* months come in 1-based, hours and minutes come in 0-based */ + /* The base time is Jan 1, 00:00 of TS_BASE_YEAR */ + years_since_base = year - TS_BASE_YEAR; + months_since_base = years_since_base * TS_MONTHS_PER_YEAR + (month - 1); + days_since_base = months_since_base * TS_DAYS_PER_MONTH + (day - 1); + hours_since_base = days_since_base * 24 + hour; + minutes_since_base = hours_since_base * 60 + min; + TS_PACK_UINT32_INTO_BYTES(minutes_since_base, ts->data); + + sec /= TS_SECOND_BYTES_BIAS; + v = (unsigned int)sec; + TS_PACK_UINT32_INTO_BYTES(v, ts->data + 4); return (PyObject *)ts; }
  29. Download patch docs/api/pickling.rst

    --- 4.2.2-3/docs/api/pickling.rst 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/docs/api/pickling.rst 2018-10-22 12:41:54.000000000 +0000 @@ -18,8 +18,8 @@ machinery happy: >>> f, (c,), state = x.__reduce__() >>> f.__name__ '__newobj__' - >>> f.__module__ - 'copy_reg' + >>> f.__module__.replace('_', '') # Normalize Python2/3 + 'copyreg' >>> c.__name__ 'Simple' @@ -57,8 +57,8 @@ by overriding :meth:`__getnewargs__`, :m >>> (f, (c, ax, ay), a) = x.__reduce__() >>> f.__name__ '__newobj__' - >>> f.__module__ - 'copy_reg' + >>> f.__module__.replace('_', '') # Normalize Python2/3 + 'copyreg' >>> c.__name__ 'Custom' >>> ax, ay, a @@ -79,7 +79,6 @@ ignores any slots which map onto the "pe .. doctest:: - >>> import copy_reg >>> from persistent.tests.cucumbers import SubSlotted >>> x = SubSlotted('x', 'y', 'z')
  30. Download patch CHANGES.rst

    --- 4.2.2-3/CHANGES.rst 2016-11-29 19:05:21.000000000 +0000 +++ 4.4.3-0ubuntu4/CHANGES.rst 2018-10-22 12:41:54.000000000 +0000 @@ -1,11 +1,147 @@ ``persistent`` Changelog ======================== +4.4.3 (2018-10-22) +------------------ + +- Fix the repr of the persistent objects to include the module name + when using the C extension. This matches the pure-Python behaviour + and the behaviour prior to 4.4.0. See `issue 92 + <https://github.com/zopefoundation/persistent/issues/92>`_. + +- Change the repr of persistent objects to format the OID as in + integer in hexadecimal notation if it is an 8-byte byte string, as + ZODB does. This eliminates some issues in doctests. See `issue 95 + <https://github.com/zopefoundation/persistent/pull/95>`_. + +4.4.2 (2018-08-28) +------------------ + +- Explicitly use unsigned constants for packing and unpacking C + timestamps, fixing an arithmetic issue for GCC when optimizations + are enabled and ``-fwrapv`` is *not* enabled. See `issue 86 + <https://github.com/zopefoundation/persistent/issues/86>`_. + + +4.4.1 (2018-08-23) +------------------ + +- Fix installation of source packages on PyPy. See `issue 88 + <https://github.com/zopefoundation/persistent/issues/88>`_. + + +4.4.0 (2018-08-22) +------------------ + +- Use unsigned constants when doing arithmetic on C timestamps, + possibly avoiding some overflow issues with some compilers or + compiler settings. See `issue 86 + <https://github.com/zopefoundation/persistent/issues/86>`_. + +- Change the default representation of ``Persistent`` objects to + include the representation of their OID and jar, if set. Also add + the ability for subclasses to implement ``_p_repr()`` instead of + overriding ``__repr__`` for better exception handling. See `issue 11 + <https://github.com/zopefoundation/persistent/issues/11>`_. + +- Reach and maintain 100% test coverage. + +- Simplify ``__init__.py``, including removal of an attempted legacy + import of ``persistent.TimeStamp``. See `PR 80 + <https://github.com/zopefoundation/persistent/pull/80>`_. + +- Add support for Python 3.7 and drop support for Python 3.3. + +- Build the CFFI modules (used on PyPy or when PURE_PYTHON is set) `at + installation or wheel building time + <https://cffi.readthedocs.io/en/latest/cdef.html#ffibuilder-set-source-preparing-out-of-line-modules>`_ + when CFFI is available. This replaces `the deprecated way + <https://cffi.readthedocs.io/en/latest/overview.html#abi-versus-api>`_ + of building them at import time. If binary wheels are distributed, + it eliminates the need to have a functioning C compiler to use PyPy. + See `issue 75 + <https://github.com/zopefoundation/persistent/issues/75>`_. + +- Fix deleting the ``_p_oid`` of a pure-Python persistent object when + it is in a cache. + +- Fix deleting special (``_p``) attributes of a pure-Python persistent + object that overrides ``__delattr__`` and correctly calls ``_p_delattr``. + +- Remove some internal compatibility shims that are no longer + necessary. See `PR 82 <https://github.com/zopefoundation/persistent/pull/82>`_. + +- Make the return value of ``TimeStamp.second()`` consistent across C + and Python implementations when the ``TimeStamp`` was created from 6 + arguments with floating point seconds. Also make it match across + trips through ``TimeStamp.raw()``. Previously, the C version could + initially have erroneous rounding and too much false precision, + while the Python version could have too much precision. The raw/repr + values have not changed. See `issue 41 + <https://github.com/zopefoundation/persistent/issues/41>`_. + +4.3.0 (2018-07-30) +------------------ + +- Fix the possibility of a rare crash in the C extension when + deallocating items. See https://github.com/zopefoundation/persistent/issues/66 + +- Change cPickleCache's comparison of object sizes to determine + whether an object can go in the cache to use ``PyObject_TypeCheck()``. + This matches what the pure Python implementation does and is a + stronger test that the object really is compatible with the cache. + Previously, an object could potentially include ``cPersistent_HEAD`` + and *not* set ``tp_base`` to ``cPersistenceCAPI->pertype`` and still + be eligible for the pickle cache; that is no longer the case. See + `issue 69 <https://github.com/zopefoundation/persistent/issues/69>`_. + +4.2.4.2 (2017-04-23) +-------------------- + +- Packaging-only release: fix Python 2.7 ``manylinux`` wheels. + + +4.2.4.1 (2017-04-21) +-------------------- + +- Packaging-only release: get ``manylinux`` wheel built automatically. + + +4.2.4 (2017-03-20) +------------------ + +- Avoid raising a ``SystemError: error return without exception set`` + when loading an object with slots whose jar generates an exception + (such as a ZODB ``POSKeyError``) in ``setstate``. + + +4.2.3 (2017-03-08) +------------------ + +- Fix the hashcode of Python ``TimeStamp`` objects on 64-bit Python on + Windows. See https://github.com/zopefoundation/persistent/pull/55 + +- Stop calling ``gc.collect`` every time ``PickleCache.incrgc`` is called (every + transaction boundary) in pure-Python mode (PyPy). This means that + the reported size of the cache may be wrong (until the next GC), but + it is much faster. This should not have any observable effects for + user code. + +- Stop clearing the dict and slots of objects added to + ``PickleCache.new_ghost`` (typically these values are passed to + ``__new__`` from the pickle data) in pure-Python mode (PyPy). This + matches the behaviour of the C code. + +- Add support for Python 3.6. + +- Fix ``__setstate__`` interning when ``state`` parameter is not a built-in dict + + 4.2.2 (2016-11-29) ------------------ - Drop use of ``ctypes`` for determining maximum integer size, to increase - pure-Python compatibility. + pure-Python compatibility. See https://github.com/zopefoundation/persistent/pull/31 - Ensure that ``__slots__`` attributes are cleared when a persistent object is ghostified. (This excluses classes that override
  31. Download patch persistent/tests/test_ring.py

    --- 4.2.2-3/persistent/tests/test_ring.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/tests/test_ring.py 2018-10-22 12:41:54.000000000 +0000 @@ -31,7 +31,7 @@ class DummyPersistent(object): if oid is None: self._p_oid = self._next_oid() - def __repr__(self): + def __repr__(self): # pragma: no cover return "<Dummy %r>" % self._p_oid class _Ring_Base(object):
  32. Download patch .gitignore

    --- 4.2.2-3/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ 4.4.3-0ubuntu4/.gitignore 2018-10-22 12:41:54.000000000 +0000 @@ -0,0 +1,18 @@ +*.pyc +*.so +__pycache__ +build +docs/_build +bin +develop-eggs +eggs +parts +.tox +.coverage +nosetests.xml +coverage.xml +*.egg-info +.installed.cfg +.dir-locals.el +dist +htmlcov
  33. Download patch persistent/persistence.py

    --- 4.2.2-3/persistent/persistence.py 2016-11-29 19:03:33.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/persistence.py 2018-10-22 12:41:54.000000000 +0000 @@ -11,7 +11,7 @@ # FOR A PARTICULAR PURPOSE. # ############################################################################## -import sys +import struct from zope.interface import implementer @@ -20,14 +20,13 @@ from persistent.interfaces import GHOST from persistent.interfaces import UPTODATE from persistent.interfaces import CHANGED from persistent.interfaces import STICKY -from persistent.interfaces import OID_TYPE + from persistent.interfaces import SERIAL_TYPE from persistent.timestamp import TimeStamp from persistent.timestamp import _ZERO from persistent._compat import copy_reg from persistent._compat import intern -from . import ring _INITIAL_SERIAL = _ZERO @@ -38,6 +37,7 @@ _STICKY = 0x0002 _OGA = object.__getattribute__ _OSA = object.__setattr__ +_ODA = object.__delattr__ # These names can be used from a ghost without causing it to be # activated. These are standardized with the C implementation @@ -52,6 +52,11 @@ SPECIAL_NAMES = ('__class__', # check in __getattribute__ _SPECIAL_NAMES = set(SPECIAL_NAMES) +# Represent 8-byte OIDs as hex integer, just like +# ZODB does. +_OID_STRUCT = struct.Struct('>Q') +_OID_UNPACK = _OID_STRUCT.unpack + @implementer(IPersistent) class Persistent(object): """ Pure Python implmentation of Persistent base class @@ -301,7 +306,7 @@ class Persistent(object): if (_OGA(self, '_Persistent__jar') is not None and _OGA(self, '_Persistent__oid') is not None): _OGA(self, '_p_register')() - object.__delattr__(self, name) + _ODA(self, name) def _slotnames(self, _v_exclude=True): slotnames = copy_reg._slotnames(type(self)) @@ -413,27 +418,32 @@ class Persistent(object): _OSA(self, '_Persistent__flags', 0) self._p_deactivate() - def _p_invalidate_deactivate_helper(self): + def _p_invalidate_deactivate_helper(self, clear=True): jar = _OGA(self, '_Persistent__jar') if jar is None: return if _OGA(self, '_Persistent__flags') is not None: _OSA(self, '_Persistent__flags', None) - idict = getattr(self, '__dict__', None) - if idict is not None: - idict.clear() - type_ = type(self) - # ( for backward-compatibility reason we release __slots__ only if - # class does not override __new__ ) - if type_.__new__ is Persistent.__new__: - for slotname in Persistent._slotnames(self, _v_exclude=False): - try: - getattr(type_, slotname).__delete__(self) - except AttributeError: - # AttributeError means slot variable was not initialized at all - - # - we can simply skip its deletion. - pass + + if clear: + try: + idict = _OGA(self, '__dict__') + except AttributeError: + pass + else: + idict.clear() + type_ = type(self) + # for backward-compatibility reason we release __slots__ only if + # class does not override __new__ + if type_.__new__ is Persistent.__new__: + for slotname in Persistent._slotnames(self, _v_exclude=False): + try: + getattr(type_, slotname).__delete__(self) + except AttributeError: + # AttributeError means slot variable was not initialized at all - + # - we can simply skip its deletion. + pass # Implementation detail: deactivating/invalidating # updates the size of the cache (if we have one) @@ -462,7 +472,7 @@ class Persistent(object): """ See IPersistent. """ if name.startswith('_p_'): - setattr(self, name, value) + _OSA(self, name, value) return True self._p_activate() self._p_accessed() @@ -472,7 +482,12 @@ class Persistent(object): """ See IPersistent. """ if name.startswith('_p_'): - delattr(self, name) + if name == '_p_oid' and self._p_is_in_cache(_OGA(self, '_Persistent__jar')): + # The C implementation forbids deleting the oid + # if we're already in a cache. Match its error message + raise ValueError('can not change _p_jar of cached object') + + _ODA(self, name) return True self._p_activate() self._p_accessed() @@ -547,6 +562,44 @@ class Persistent(object): if cache is not None: return cache.get(oid) is self + def __repr__(self): + p_repr_str = '' + p_repr = getattr(type(self), '_p_repr', None) + if p_repr is not None: + try: + return p_repr(self) + except Exception as e: + p_repr_str = ' _p_repr %r' % (e,) + + oid = _OGA(self, '_Persistent__oid') + jar = _OGA(self, '_Persistent__jar') + + oid_str = '' + jar_str = '' + + if oid is not None: + try: + if isinstance(oid, bytes) and len(oid) == 8: + oid_str = ' oid 0x%x' % (_OID_UNPACK(oid)[0],) + else: + oid_str = ' oid %r' % (oid,) + except Exception as e: + oid_str = ' oid %r' % (e,) + + if jar is not None: + try: + jar_str = ' in %r' % (jar,) + except Exception as e: + jar_str = ' in %r' % (e,) + + return '<%s.%s object at 0x%x%s%s%s>' % ( + # Match the C name for this exact class + type(self).__module__ if type(self) is not Persistent else 'persistent', + type(self).__name__, id(self), + oid_str, jar_str, p_repr_str + ) + + def _estimated_size_in_24_bits(value): if value > 1073741696: return 16777215
  34. Download patch persistent/__init__.py

    --- 4.2.2-3/persistent/__init__.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/__init__.py 2018-10-22 12:41:54.000000000 +0000 @@ -15,55 +15,49 @@ Fall back to pure Python implementations. """ -import os -PURE_PYTHON = os.environ.get('PURE_PYTHON') -if not PURE_PYTHON: - try: - from persistent.cPersistence import Persistent - from persistent.cPersistence import GHOST - from persistent.cPersistence import UPTODATE - from persistent.cPersistence import CHANGED - from persistent.cPersistence import STICKY - from persistent.cPersistence import simple_new - except ImportError: #pragma NO COVER - from persistent.persistence import Persistent - from persistent.persistence import GHOST - from persistent.persistence import UPTODATE - from persistent.persistence import CHANGED - from persistent.persistence import STICKY - else: - from persistent._compat import copy_reg - copy_reg.constructor(simple_new) - # Make an interface declaration for Persistent, if zope.interface - # is available. Note that the Python version already does this. - try: - from zope.interface import classImplements - except ImportError: #pragma NO COVER - pass - else: - from persistent.interfaces import IPersistent - classImplements(Persistent, IPersistent) - - try: - from persistent.cPickleCache import PickleCache - except ImportError: #pragma NO COVER - from persistent.picklecache import PickleCache - - try: - import persistent.TimeStamp - except ImportError: #pragma NO COVER - import persistent.timestamp as TimeStamp - import sys - sys.modules['persistent.TimeStamp' - ] = sys.modules['persistent.timestamp'] -else: #pragma NO COVER - from persistent.persistence import Persistent - from persistent.persistence import GHOST - from persistent.persistence import UPTODATE - from persistent.persistence import CHANGED - from persistent.persistence import STICKY - from persistent.picklecache import PickleCache - import persistent.timestamp as TimeStamp - import sys - sys.modules['persistent.TimeStamp'] = sys.modules['persistent.timestamp'] +import sys + +__all__ = [ + 'IPersistent', + 'Persistent', + 'GHOST', + 'UPTODATE', + 'CHANGED', + 'STICKY', + 'PickleCache', + 'TimeStamp', +] +from persistent._compat import PURE_PYTHON +from persistent.interfaces import IPersistent + +import persistent.timestamp as TimeStamp + +from persistent import persistence as pyPersistence +from persistent import picklecache as pyPickleCache + +try: + # Be careful not to shadow the modules + from persistent import cPersistence as _cPersistence + from persistent import cPickleCache as _cPickleCache +except ImportError: # pragma: no cover + _cPersistence = None + _cPickleCache = None +else: + # Make an interface declaration for Persistent + # Note that the Python version already does this. + from zope.interface import classImplements + classImplements(_cPersistence.Persistent, IPersistent) + + +_persistence = pyPersistence if PURE_PYTHON or _cPersistence is None else _cPersistence +_picklecache = pyPickleCache if PURE_PYTHON or _cPickleCache is None else _cPickleCache + +Persistent = _persistence.Persistent +GHOST = _persistence.GHOST +UPTODATE = _persistence.UPTODATE +CHANGED = _persistence.CHANGED +STICKY = _persistence.STICKY +PickleCache = _picklecache.PickleCache + +sys.modules['persistent.TimeStamp'] = sys.modules['persistent.timestamp']
  35. Download patch .manylinux-install.sh

    --- 4.2.2-3/.manylinux-install.sh 1970-01-01 00:00:00.000000000 +0000 +++ 4.4.3-0ubuntu4/.manylinux-install.sh 2018-10-22 12:41:54.000000000 +0000 @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -e -x + +# Compile wheels +for PYBIN in /opt/python/*/bin; do + if [[ "${PYBIN}" == *"cp27"* ]] || \ + [[ "${PYBIN}" == *"cp34"* ]] || \ + [[ "${PYBIN}" == *"cp35"* ]] || \ + [[ "${PYBIN}" == *"cp36"* ]] || \ + [[ "${PYBIN}" == *"cp37"* ]]; then + "${PYBIN}/pip" install -U pip setuptools wheel cffi + "${PYBIN}/pip" install -e /io/ + "${PYBIN}/pip" wheel /io/ -w wheelhouse/ + rm -rf /io/build /io/*.egg-info + fi +done + +# Bundle external shared libraries into the wheels +for whl in wheelhouse/persistent*.whl; do + auditwheel repair "$whl" -w /io/wheelhouse/ +done
  36. Download patch persistent/tests/test_mapping.py

    --- 4.2.2-3/persistent/tests/test_mapping.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/tests/test_mapping.py 2018-10-22 12:41:54.000000000 +0000 @@ -13,7 +13,7 @@ ############################################################################## import unittest - +# pylint:disable=blacklisted-name, protected-access class Test_default(unittest.TestCase): @@ -25,15 +25,14 @@ class Test_default(unittest.TestCase): return self._getTargetClass()(func) def test___get___from_class(self): - _called_with = [] def _test(inst): - _called_with.append(inst) - return '_test' + raise AssertionError("Must not be caled") + descr = self._makeOne(_test) class Foo(object): testing = descr - self.assertTrue(Foo.testing is descr) - self.assertEqual(_called_with, []) + self.assertIs(Foo.testing, descr) + def test___get___from_instance(self): _called_with = [] @@ -86,9 +85,9 @@ class PersistentMappingTests(unittest.Te def __init__(self, initmapping): self.__data = initmapping def items(self): - return self.__data.items() - v0 = self._makeOne(OtherMapping(u0)) - vv = self._makeOne([(0, 0), (1, 1)]) + raise AssertionError("Not called") + self._makeOne(OtherMapping(u0)) + self._makeOne([(0, 0), (1, 1)]) # Test __repr__ eq = self.assertEqual @@ -97,24 +96,37 @@ class PersistentMappingTests(unittest.Te eq(repr(u1), repr(l1), "repr(u1) == repr(l1)") # Test __cmp__ and __len__ - - if PYTHON2: - def mycmp(a, b): - r = cmp(a, b) - if r < 0: return -1 - if r > 0: return 1 - return r - - all = [l0, l1, l2, u, u0, u1, u2, uu, uu0, uu1, uu2] - for a in all: - for b in all: - eq(mycmp(a, b), mycmp(len(a), len(b)), - "mycmp(a, b) == mycmp(len(a), len(b))") + try: + cmp + except NameError: + def cmp(a, b): + if a == b: + return 0 + if hasattr(a, 'items'): + a = sorted(a.items()) + b = sorted(b.items()) + if a < b: + return -1 + return 1 + + def mycmp(a, b): + r = cmp(a, b) + if r < 0: + return -1 + if r > 0: + return 1 + return r + + to_test = [l0, l1, l2, u, u0, u1, u2, uu, uu0, uu1, uu2] + for a in to_test: + for b in to_test: + eq(mycmp(a, b), mycmp(len(a), len(b)), + "mycmp(a, b) == mycmp(len(a), len(b))") # Test __getitem__ - for i in range(len(u2)): - eq(u2[i], i, "u2[i] == i") + for i, val in enumerate(u2): + eq(val, i, "u2[i] == i") # Test get @@ -136,12 +148,8 @@ class PersistentMappingTests(unittest.Te del uu2[1] del uu2[0] - try: + with self.assertRaises(KeyError): del uu2[0] - except KeyError: - pass - else: - raise TestFailed("uu2[0] shouldn't be deletable") # Test __contains__ for i in u2: @@ -176,12 +184,8 @@ class PersistentMappingTests(unittest.Te eq(x, 1, "u2.pop(1) == 1") self.assertTrue(1 not in u2, "1 not in u2") - try: + with self.assertRaises(KeyError): u2.pop(1) - except KeyError: - pass - else: - self.fail("1 should not be poppable from u2") x = u2.pop(1, 7) eq(x, 7, "u2.pop(1, 7) == 7") @@ -230,8 +234,4 @@ class Test_legacy_PersistentDict(unittes def test_suite(): - return unittest.TestSuite(( - unittest.makeSuite(Test_default), - unittest.makeSuite(PersistentMappingTests), - unittest.makeSuite(Test_legacy_PersistentDict), - )) + return unittest.defaultTestLoader.loadTestsFromName(__name__)
  37. Download patch persistent/picklecache.py

    --- 4.2.2-3/persistent/picklecache.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/picklecache.py 2018-10-22 12:41:54.000000000 +0000 @@ -13,7 +13,7 @@ ############################################################################## import gc import weakref -import sys + from zope.interface import implementer @@ -21,31 +21,13 @@ from persistent.interfaces import GHOST from persistent.interfaces import IPickleCache from persistent.interfaces import OID_TYPE from persistent.interfaces import UPTODATE -from persistent import Persistent +from persistent.persistence import Persistent from persistent.persistence import _estimated_size_in_24_bits # Tests may modify this to add additional types _CACHEABLE_TYPES = (type, Persistent) _SWEEPABLE_TYPES = (Persistent,) -# The Python PickleCache implementation keeps track of the objects it -# is caching in a WeakValueDictionary. The number of objects in the -# cache (in this dictionary) is exposed as the len of the cache. Under -# non-refcounted implementations like PyPy, the weak references in -# this dictionary are only cleared when the garbage collector runs. -# Thus, after an incrgc, the len of the cache is incorrect for some -# period of time unless we ask the GC to run. -# Furthermore, evicted objects can stay in the dictionary and be returned -# from __getitem__ or possibly conflict with a new item in __setitem__. -# We determine whether or not we need to do the GC based on the ability -# to get a reference count: PyPy and Jython don't use refcounts and don't -# expose this; this is safer than blacklisting specific platforms (e.g., -# what about IronPython?). On refcounted platforms, we don't want to -# run a GC to avoid possible performance regressions (e.g., it may -# block all threads). -# Tests may modify this -_SWEEP_NEEDS_GC = not hasattr(sys, 'getrefcount') - # On Jython, we need to explicitly ask it to monitor # objects if we want a more deterministic GC if hasattr(gc, 'monitorObject'): # pragma: no cover @@ -264,7 +246,7 @@ class PickleCache(object): # careful to avoid broken _p_invalidate and _p_deactivate # that don't call the super class. See ZODB's # testConnection.doctest_proper_ghost_initialization_with_empty__p_deactivate - obj._p_invalidate_deactivate_helper() + obj._p_invalidate_deactivate_helper(False) self[oid] = obj def reify(self, to_reify): @@ -381,11 +363,7 @@ class PickleCache(object): ejected = len(to_eject) if ejected: self.ring.delete_all(to_eject) - del to_eject # Got to clear our local if we want the GC to get the weak refs - if ejected and _SWEEP_NEEDS_GC: - # See comments on _SWEEP_NEEDS_GC - gc.collect() return ejected @_sweeping_ring
  38. Download patch persistent/tests/test_persistence.py
  39. Download patch setup.cfg

    --- 4.2.2-3/setup.cfg 2016-11-29 19:07:54.000000000 +0000 +++ 4.4.3-0ubuntu4/setup.cfg 2018-10-22 12:41:54.000000000 +0000 @@ -1,18 +1,3 @@ -[nosetests] -nocapture = 1 -cover-package = persistent -cover-erase = 1 -cover-branches = 1 -cover-min-percentage = 100 -with-doctest = 0 -where = persistent - [aliases] dev = develop easy_install persistent[testing] docs = develop easy_install persistent[docs] - -[egg_info] -tag_build = -tag_date = 0 -tag_svn_revision = 0 -
  40. Download patch persistent/tests/test_docs.py

    --- 4.2.2-3/persistent/tests/test_docs.py 1970-01-01 00:00:00.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/tests/test_docs.py 2018-10-22 12:41:54.000000000 +0000 @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2001, 2002 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +""" +Tests for the documentation. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +# disable: accessing protected members, too many methods +# pylint: disable=W0212,R0904 + +import os.path +import unittest +import doctest + +import manuel.capture +import manuel.codeblock +import manuel.doctest +import manuel.ignore +import manuel.testing + + +def test_suite(): + here = os.path.dirname(os.path.abspath(__file__)) + while not os.path.exists(os.path.join(here, 'setup.py')): + prev, here = here, os.path.dirname(here) + if here == prev: + # Let's avoid infinite loops at root + raise AssertionError('could not find my setup.py') + + docs = os.path.join(here, 'docs', 'api') + + files_to_test = ( + 'cache.rst', + 'attributes.rst', + 'pickling.rst', + ) + paths = [os.path.join(docs, f) for f in files_to_test] + + m = manuel.ignore.Manuel() + m += manuel.doctest.Manuel(optionflags=( + doctest.NORMALIZE_WHITESPACE + | doctest.ELLIPSIS + | doctest.IGNORE_EXCEPTION_DETAIL + )) + m += manuel.codeblock.Manuel() + m += manuel.capture.Manuel() + + suite = unittest.TestSuite() + suite.addTest( + manuel.testing.TestSuite( + m, + *paths + ) + ) + + return suite
  41. Download patch persistent.egg-info/requires.txt

    --- 4.2.2-3/persistent.egg-info/requires.txt 2016-11-29 19:07:54.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent.egg-info/requires.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -zope.interface - -[docs] -Sphinx -repoze.sphinx.autointerface - -[test] - -[testing] -nose -coverage
  42. Download patch .manylinux.sh

    --- 4.2.2-3/.manylinux.sh 1970-01-01 00:00:00.000000000 +0000 +++ 4.4.3-0ubuntu4/.manylinux.sh 2018-10-22 12:41:54.000000000 +0000 @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e -x + +docker pull $DOCKER_IMAGE + +docker run --rm -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD /io/.manylinux-install.sh + +pip install twine && twine upload -u zope.wheelbuilder -p $PYPIPASSWORD wheelhouse/*
  43. Download patch setup.py

    --- 4.2.2-3/setup.py 2016-11-29 19:06:11.000000000 +0000 +++ 4.4.3-0ubuntu4/setup.py 2018-10-22 12:41:54.000000000 +0000 @@ -12,17 +12,16 @@ # ############################################################################## -__version__ = '4.2.2' - import os import platform import sys - from setuptools import Extension from setuptools import find_packages from setuptools import setup +version = '4.4.3' + here = os.path.abspath(os.path.dirname(__file__)) @@ -33,84 +32,105 @@ def _read_file(filename): README = (_read_file('README.rst') + '\n\n' + _read_file('CHANGES.rst')) -py_impl = getattr(platform, 'python_implementation', lambda: None) -is_pypy = py_impl() == 'PyPy' +is_pypy = platform.python_implementation() == 'PyPy' is_jython = 'java' in sys.platform -is_pure = os.environ.get('PURE_PYTHON') # Jython cannot build the C optimizations, while on PyPy they are # anti-optimizations (the C extension compatibility layer is known-slow, # and defeats JIT opportunities). -if is_pypy or is_jython or is_pure: - ext_modules = headers = [] +if is_pypy or is_jython: + # Note that all the lists we pass to setuptools must be distinct + # objects, or bad things happen. See https://github.com/zopefoundation/persistent/issues/88 + ext_modules = [] + headers = [] else: - ext_modules = [Extension(name = 'persistent.cPersistence', - sources= ['persistent/cPersistence.c', - 'persistent/ring.c', - ], - depends = ['persistent/cPersistence.h', - 'persistent/ring.h', - 'persistent/ring.c', - ] - ), - Extension(name = 'persistent.cPickleCache', - sources= ['persistent/cPickleCache.c', - 'persistent/ring.c' - ], - depends = ['persistent/cPersistence.h', - 'persistent/ring.h', - 'persistent/ring.c', - ] - ), - Extension(name = 'persistent._timestamp', - sources= ['persistent/_timestamp.c', - ], - ), - ] - headers = ['persistent/cPersistence.h', - 'persistent/ring.h'] + ext_modules = [ + Extension( + name='persistent.cPersistence', + sources=[ + 'persistent/cPersistence.c', + 'persistent/ring.c', + ], + depends=[ + 'persistent/cPersistence.h', + 'persistent/ring.h', + 'persistent/ring.c', + ] + ), + Extension( + name='persistent.cPickleCache', + sources=[ + 'persistent/cPickleCache.c', + 'persistent/ring.c', + ], + depends=[ + 'persistent/cPersistence.h', + 'persistent/ring.h', + 'persistent/ring.c', + ] + ), + Extension( + name='persistent._timestamp', + sources=[ + 'persistent/_timestamp.c', + ], + ), + ] + headers = [ + 'persistent/cPersistence.h', + 'persistent/ring.h', + ] setup(name='persistent', - version=__version__, + version=version, description='Translucent persistent objects', long_description=README, classifiers=[ - "Development Status :: 6 - Mature", - "License :: OSI Approved :: Zope Public License", - "Programming Language :: Python", - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Framework :: ZODB", - "Topic :: Database", - "Topic :: Software Development :: Libraries :: Python Modules", - "Operating System :: Microsoft :: Windows", - "Operating System :: Unix", - ], + "Development Status :: 6 - Mature", + "License :: OSI Approved :: Zope Public License", + "Programming Language :: Python", + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Framework :: ZODB", + "Topic :: Database", + "Topic :: Software Development :: Libraries :: Python Modules", + "Operating System :: Microsoft :: Windows", + "Operating System :: Unix", + ], author="Zope Corporation", author_email="zodb-dev@zope.org", - url="http://www.zope.org/Products/ZODB", + url="https://github.com/zopefoundation/persistent/", license="ZPL 2.1", platforms=["any"], - packages=find_packages(), + # Make sure we don't get 'terryfy' included in wheels + # created on macOS CI + packages=find_packages(include=("persistent",)), include_package_data=True, zip_safe=False, - ext_modules = ext_modules, - headers = headers, - extras_require = { - 'test': (), - 'testing': ['nose', 'coverage'], - 'docs': ['Sphinx', 'repoze.sphinx.autointerface'], + ext_modules=ext_modules, + cffi_modules=['persistent/_ring_build.py:ffi'], + headers=headers, + extras_require={ + 'test': [ + 'zope.testrunner', + "cffi ; platform_python_implementation == 'CPython'", + 'manuel', + ], + 'testing': (), + 'docs': [ + 'Sphinx', + 'repoze.sphinx.autointerface', + ], }, - test_suite="persistent.tests", install_requires=[ - 'zope.interface', + 'zope.interface', ], - entry_points = """\ - """ - ) + entry_points={}, +)
  44. Download patch persistent/tests/test_picklecache.py
  45. Download patch persistent.egg-info/SOURCES.txt

    --- 4.2.2-3/persistent.egg-info/SOURCES.txt 2016-11-29 19:07:54.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent.egg-info/SOURCES.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -.coveragerc -.travis.yml -CHANGES.rst -COPYRIGHT.txt -LICENSE.txt -MANIFEST.in -README.rst -bootstrap.py -buildout.cfg -rtd.txt -setup.cfg -setup.py -tox.ini -docs/Makefile -docs/api.rst -docs/conf.py -docs/glossary.rst -docs/index.rst -docs/make.bat -docs/using.rst -docs/api/attributes.rst -docs/api/cache.rst -docs/api/interfaces.rst -docs/api/pickling.rst -persistent/__init__.py -persistent/_compat.h -persistent/_compat.py -persistent/_timestamp.c -persistent/cPersistence.c -persistent/cPersistence.h -persistent/cPickleCache.c -persistent/dict.py -persistent/interfaces.py -persistent/list.py -persistent/mapping.py -persistent/persistence.py -persistent/picklecache.py -persistent/ring.c -persistent/ring.h -persistent/ring.py -persistent/timestamp.py -persistent/wref.py -persistent.egg-info/PKG-INFO -persistent.egg-info/SOURCES.txt -persistent.egg-info/dependency_links.txt -persistent.egg-info/entry_points.txt -persistent.egg-info/not-zip-safe -persistent.egg-info/requires.txt -persistent.egg-info/top_level.txt -persistent/tests/__init__.py -persistent/tests/attrhooks.py -persistent/tests/cucumbers.py -persistent/tests/test_list.py -persistent/tests/test_mapping.py -persistent/tests/test_persistence.py -persistent/tests/test_picklecache.py -persistent/tests/test_ring.py -persistent/tests/test_timestamp.py -persistent/tests/test_wref.py -persistent/tests/utils.py \ No newline at end of file
  46. Download patch persistent/wref.py

    --- 4.2.2-3/persistent/wref.py 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/wref.py 2018-10-22 12:41:54.000000000 +0000 @@ -83,7 +83,7 @@ class PersistentWeakKeyDictionary(Persis self.update(adict) # XXX 'kwargs' is pointless, because keys must be strings, but we # are going to try (and fail) to wrap a WeakRef around them. - if kwargs: #pragma NO COVER + if kwargs: # pragma: no cover self.update(kwargs) def __getstate__(self):
  47. Download patch appveyor.yml

    --- 4.2.2-3/appveyor.yml 1970-01-01 00:00:00.000000000 +0000 +++ 4.4.3-0ubuntu4/appveyor.yml 2018-10-22 12:41:54.000000000 +0000 @@ -0,0 +1,37 @@ +environment: + global: + TWINE_USERNAME: zope.wheelbuilder + TWINE_PASSWORD: + secure: UcdTh6W78cRLVGfKRFoa5A== + + matrix: + - python: 27 + - python: 27-x64 + - python: 34 + - python: 34-x64 + - python: 35 + - python: 35-x64 + - python: 36 + - python: 36-x64 + - python: 37 + - python: 37-x64 +install: + - "SET PATH=C:\\Python%PYTHON%;c:\\Python%PYTHON%\\scripts;%PATH%" + - echo "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 > "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\amd64\vcvars64.bat" + - python -m pip install -U pip setuptools wheel cffi + - pip install -e .[test] + +build_script: + - python -W ignore setup.py -q bdist_wheel + +test_script: + - python -m zope.testrunner --test-path=. + +artifacts: + - path: 'dist\*.whl' + name: wheel + +deploy_script: + - ps: if ($env:APPVEYOR_REPO_TAG -eq $TRUE) { pip install twine; twine upload dist/* } + +deploy: on
  48. Download patch persistent/cPersistence.c
  49. Download patch persistent/tests/test_list.py
  50. Download patch persistent/cPickleCache.c

    --- 4.2.2-3/persistent/cPickleCache.c 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/cPickleCache.c 2018-10-22 12:41:54.000000000 +0000 @@ -99,11 +99,6 @@ static char cPickleCache_doc_string[] = #include <stddef.h> #undef Py_FindMethod -/* Python 2.4 backward compat */ -#if PY_MAJOR_VERSION <= 2 && PY_MINOR_VERSION < 5 -#define Py_ssize_t int -typedef Py_ssize_t (*lenfunc)(PyObject *); -#endif /* Python string objects to speed lookups; set by module init. */ static PyObject *py__p_changed; @@ -111,7 +106,7 @@ static PyObject *py__p_deactivate; static PyObject *py__p_jar; static PyObject *py__p_oid; -static cPersistenceCAPIstruct *capi; +static cPersistenceCAPIstruct *cPersistenceCAPI; /* This object is the pickle cache. The CACHE_HEAD macro guarantees that layout of this struct is the same as the start of @@ -206,7 +201,7 @@ scan_gc_items(ccobject *self, int target { assert(self->ring_lock); assert(here != &self->ring_home); - + /* At this point we know that the ring only contains nodes from persistent objects, plus our own home node. We know this because the ring lock is held. We can safely assume @@ -214,14 +209,14 @@ scan_gc_items(ccobject *self, int target is not the home */ object = OBJECT_FROM_RING(self, here); - if (object->state == cPersistent_UPTODATE_STATE) + if (object->state == cPersistent_UPTODATE_STATE) { CPersistentRing placeholder; PyObject *method; PyObject *temp; int error_occurred = 0; /* deactivate it. This is the main memory saver. */ - + /* Add a placeholder, a dummy node in the ring. We need to do this to mark our position in the ring. It is possible that the PyObject_GetAttr() call below will @@ -237,7 +232,7 @@ scan_gc_items(ccobject *self, int target method = PyObject_GetAttr((PyObject *)object, py__p_deactivate); if (method == NULL) error_occurred = 1; - else + else { temp = PyObject_CallObject(method, NULL); Py_DECREF(method); @@ -523,7 +518,7 @@ cc_debug_info(ccobject *self) v = Py_BuildValue("Oi", k, v->ob_refcnt); else if (! PyType_Check(v) && - (v->ob_type->tp_basicsize >= sizeof(cPersistentObject)) + PER_TypeCheck(v) ) v = Py_BuildValue("Oisi", k, v->ob_refcnt, v->ob_type->tp_name, @@ -711,13 +706,12 @@ cc_new_ghost(ccobject *self, PyObject *a { /* Its a persistent class, such as a ZClass. Thats ok. */ } - else if (v->ob_type->tp_basicsize < sizeof(cPersistentObject)) + else if (! PER_TypeCheck(v)) { /* If it's not an instance of a persistent class, (ie Python classes that derive from persistent.Persistent, BTrees, etc), report an error. - TODO: checking sizeof() seems a poor test. */ PyErr_SetString(PyExc_TypeError, "Cache values must be persistent objects."); @@ -891,6 +885,7 @@ cc_init(ccobject *self, PyObject *args, static void cc_dealloc(ccobject *self) { + PyObject_GC_UnTrack((PyObject *)self); Py_XDECREF(self->data); Py_XDECREF(self->jar); PyObject_GC_Del(self); @@ -1033,13 +1028,11 @@ cc_add_item(ccobject *self, PyObject *ke { /* Its a persistent class, such as a ZClass. Thats ok. */ } - else if (v->ob_type->tp_basicsize < sizeof(cPersistentObject)) + else if (! PER_TypeCheck(v)) { /* If it's not an instance of a persistent class, (ie Python classes that derive from persistent.Persistent, BTrees, etc), report an error. - - TODO: checking sizeof() seems a poor test. */ PyErr_SetString(PyExc_TypeError, "Cache values must be persistent objects."); @@ -1058,7 +1051,7 @@ cc_add_item(ccobject *self, PyObject *ke PyErr_Format(PyExc_TypeError, "Cached object oid must be bytes, not a %s", oid->ob_type->tp_name); - + return -1; } @@ -1346,14 +1339,14 @@ module_init(void) #endif #ifdef PY3K - capi = (cPersistenceCAPIstruct *)PyCapsule_Import(CAPI_CAPSULE_NAME, 0); + cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCapsule_Import(CAPI_CAPSULE_NAME, 0); #else - capi = (cPersistenceCAPIstruct *)PyCObject_Import( - "persistent.cPersistence", "CAPI"); + cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCObject_Import( + "persistent.cPersistence", "CAPI"); #endif - if (!capi) + if (!cPersistenceCAPI) return NULL; - capi->percachedel = (percachedelfunc)cc_oid_unreferenced; + cPersistenceCAPI->percachedel = (percachedelfunc)cc_oid_unreferenced; py__p_changed = INTERN("_p_changed"); if (!py__p_changed)
  51. Download patch persistent/tests/test_timestamp.py
  52. Download patch persistent/_ring_build.py

    --- 4.2.2-3/persistent/_ring_build.py 1970-01-01 00:00:00.000000000 +0000 +++ 4.4.3-0ubuntu4/persistent/_ring_build.py 2018-10-22 12:41:54.000000000 +0000 @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2018 Zope Foundation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +from __future__ import absolute_import, print_function, division + +import os +from cffi import FFI + +this_dir = os.path.dirname(os.path.abspath(__file__)) + +ffi = FFI() +with open(os.path.join(this_dir, 'ring.h')) as f: + ffi.cdef(f.read()) + +ffi.set_source('persistent._ring', + '#include "ring.c"', + include_dirs=[this_dir]) + +if __name__ == '__main__': + ffi.compile()
  53. Download patch docs/api/interfaces.rst

    --- 4.2.2-3/docs/api/interfaces.rst 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/docs/api/interfaces.rst 2018-10-22 12:41:54.000000000 +0000 @@ -15,3 +15,12 @@ :members: :member-order: bysource +Implementations +=============== + +This package provides one implementation of :class:`IPersistent` that +should be extended. + + .. autoclass:: persistent.Persistent + :members: + :show-inheritance:
  54. Download patch docs/api.rst

    --- 4.2.2-3/docs/api.rst 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/docs/api.rst 2018-10-22 12:41:54.000000000 +0000 @@ -5,6 +5,7 @@ :maxdepth: 2 api/interfaces + api/collections api/attributes api/pickling api/cache
  55. Download patch docs/using.rst

    --- 4.2.2-3/docs/using.rst 2015-09-20 14:51:12.000000000 +0000 +++ 4.4.3-0ubuntu4/docs/using.rst 2018-10-22 12:41:54.000000000 +0000 @@ -1,9 +1,10 @@ -Using :mod:`persistent` in your application -=========================================== +============================================= + Using :mod:`persistent` in your application +============================================= Inheriting from :class:`persistent.Persistent` ----------------------------------------------- +============================================== The basic mechanism for making your application's objects persistent is mix-in interitance. Instances whose classes derive from @@ -14,7 +15,7 @@ they have been changed. Relationship to a Data Manager and its Cache --------------------------------------------- +============================================ Except immediately after their creation, persistent objects are normally associated with a :term:`data manager` (also referred to as a :term:`jar`). @@ -63,7 +64,7 @@ The examples below use a stub data manag Persistent objects without a Data Manager ------------------------------------------ +========================================= Before aersistent instance has been associtated with a a data manager ( i.e., its ``_p_jar`` is still ``None``). @@ -166,7 +167,7 @@ Try all sorts of different ways to chang Associating an Object with a Data Manager ------------------------------------------ +========================================= Once associated with a data manager, a persistent object's behavior changes: @@ -212,12 +213,17 @@ manager. Subsequent modifications don't Object which register themselves with the data manager are candidates for storage to the backing store at a later point in time. +Note that mutating a non-persistent attribute of a persistent object +such as a :class:`dict` or :class:`list` will *not* cause the +containing object to be changed. Instead you can either explicitly +control the state as described below, or use a +:class:`~.PersistentList` or :class:`~.PersistentMapping`. Explicitly controlling ``_p_state`` ------------------------------------ +=================================== Persistent objects expose three methods for moving an object into and out -of the "ghost" state:: :meth:`persistent.Persistent._p_activate`, +of the "ghost" state:: :meth:`persistent.Persistent._p_activate`, :meth:`persistent.Persistent._p_activate_p_deactivate`, and :meth:`persistent.Persistent._p_invalidate`: @@ -323,7 +329,7 @@ which is exactly the same as calling ``_ The pickling protocol ---------------------- +===================== Because persistent objects need to control how they are pickled and unpickled, the :class:`persistent.Persistent` base class overrides @@ -377,7 +383,7 @@ The ``_p_serial`` attribute is not affec Estimated Object Size ---------------------- +===================== We can store a size estimation in ``_p_estimated_size``. Its default is 0. The size estimation can be used by a cache associated with the data manager @@ -407,23 +413,24 @@ Of course, the estimated size must not b Overriding the attribute protocol ---------------------------------- +================================= Subclasses which override the attribute-management methods provided by :class:`persistent.Persistent`, but must obey some constraints: -:meth:`__getattribute__`` + +:meth:`__getattribute__` When overriding ``__getattribute__``, the derived class implementation - **must** first call :meth:`persistent.Persistent._p_getattr`, passing the + **must** first call :meth:`persistent.IPersistent._p_getattr`, passing the name being accessed. This method ensures that the object is activated, if needed, and handles the "special" attributes which do not require - activation (e.g., ``_p_oid``, ``__class__``, ``__dict__``, etc.) + activation (e.g., ``_p_oid``, ``__class__``, ``__dict__``, etc.) If ``_p_getattr`` returns ``True``, the derived class implementation **must** delegate to the base class implementation for the attribute. :meth:`__setattr__` When overriding ``__setattr__``, the derived class implementation - **must** first call :meth:`persistent.Persistent._p_setattr`, passing the + **must** first call :meth:`persistent.IPersistent._p_setattr`, passing the name being accessed and the value. This method ensures that the object is activated, if needed, and handles the "special" attributes which do not require activation (``_p_*``). If ``_p_setattr`` returns ``True``, the @@ -432,7 +439,7 @@ Subclasses which override the attribute- :meth:`__detattr__` When overriding ``__detattr__``, the derived class implementation - **must** first call :meth:`persistent.Persistent._p_detattr`, passing the + **must** first call :meth:`persistent.IPersistent._p_detattr`, passing the name being accessed. This method ensures that the object is activated, if needed, and handles the "special" attributes which do not require activation (``_p_*``). If ``_p_delattr`` returns ``True``, the @@ -440,5 +447,26 @@ Subclasses which override the attribute- base class. :meth:`__getattr__` - For the `__getattr__` method, the behavior is like that for regular Python + For the ``__getattr__`` method, the behavior is like that for regular Python classes and for earlier versions of ZODB 3. + + +Implementing ``_p_repr`` +======================== + +Subclasses can implement ``_p_repr`` to provide a custom +representation. If this method raises an exception, the default +representation will be used. The benefit of implementing ``_p_repr`` +instead of overriding ``__repr__`` is that it provides safer handling +for objects that can't be activated because their persistent data is +missing or their jar is closed. + +.. doctest:: + + >>> class P(Persistent): + ... def _p_repr(self): + ... return "Custom repr" + + >>> p = P() + >>> print(repr(p)) + Custom repr
  1. nose2
  2. python-cachecontrol
  3. python-persistent