diff --git a/README.rst b/README.rst index f7b848d24..45a36d7b8 100644 --- a/README.rst +++ b/README.rst @@ -109,3 +109,8 @@ More information ================ See http://zodb.org/ + + +.. image:: https://badges.gitter.im/zopefoundation/ZODB.svg + :alt: Join the chat at https://gitter.im/zopefoundation/ZODB + :target: https://gitter.im/zopefoundation/ZODB?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge \ No newline at end of file diff --git a/src/ZODB/FileStorage/format.py b/src/ZODB/FileStorage/format.py index 013f46bde..8bd772327 100644 --- a/src/ZODB/FileStorage/format.py +++ b/src/ZODB/FileStorage/format.py @@ -85,11 +85,10 @@ import logging import struct -import sys from ZODB.POSException import POSKeyError from ZODB.utils import u64, oid_repr, as_bytes - +from ZODB._compat import PY3 class CorruptedError(Exception): pass @@ -245,7 +244,7 @@ def __init__(self, oid, tid, prev, tloc, vlen, plen): if vlen: raise ValueError( "Non-zero version length. Versions aren't supported.") - + self.oid = oid self.tid = tid self.prev = prev @@ -262,7 +261,7 @@ def recordlen(self): def TxnHeaderFromString(s): res = TxnHeader(*struct.unpack(TRANS_HDR, s)) - if sys.version_info[0] >= 3: + if PY3: res.status = res.status.decode('ascii') return res diff --git a/src/ZODB/POSException.py b/src/ZODB/POSException.py index c6758acb8..edfa786f0 100644 --- a/src/ZODB/POSException.py +++ b/src/ZODB/POSException.py @@ -15,8 +15,6 @@ $Id$""" -import sys - from ZODB.utils import oid_repr, readable_tid_repr # BBB: We moved the two transactions to the transaction package @@ -37,39 +35,22 @@ def _recon(class_, state): class POSError(Exception): """Persistent object system error.""" - if sys.version_info[:2] == (2, 6): - # The 'message' attribute was deprecated for BaseException with - # Python 2.6; here we create descriptor properties to continue using it - def __set_message(self, v): - self.__dict__['message'] = v - - def __get_message(self): - return self.__dict__['message'] - - def __del_message(self): - del self.__dict__['message'] - - message = property(__get_message, __set_message, __del_message) - - if sys.version_info[:2] >= (2, 5): - def __reduce__(self): - # Copy extra data from internal structures - state = self.__dict__.copy() - if sys.version_info[:2] == (2, 5): - state['message'] = self.message - state['args'] = self.args - - return (_recon, (self.__class__, state)) - - def __setstate__(self, state): - # PyPy doesn't store the 'args' attribute in an instance's - # __dict__; instead, it uses what amounts to a slot. Because - # we customize the pickled representation to just be a dictionary, - # the args would then get lost, leading to unprintable exceptions - # and worse. Manually assign to args from the state to be sure - # this doesn't happen. - super(POSError,self).__setstate__(state) - self.args = state['args'] + def __reduce__(self): + # Copy extra data from internal structures + state = self.__dict__.copy() + state['args'] = self.args + + return (_recon, (self.__class__, state)) + + def __setstate__(self, state): + # PyPy doesn't store the 'args' attribute in an instance's + # __dict__; instead, it uses what amounts to a slot. Because + # we customize the pickled representation to just be a dictionary, + # the args would then get lost, leading to unprintable exceptions + # and worse. Manually assign to args from the state to be sure + # this doesn't happen. + super(POSError,self).__setstate__(state) + self.args = state['args'] class POSKeyError(POSError, KeyError): """Key not found in database.""" diff --git a/src/ZODB/_compat.py b/src/ZODB/_compat.py index 8476f7890..5a72c90e7 100644 --- a/src/ZODB/_compat.py +++ b/src/ZODB/_compat.py @@ -82,7 +82,7 @@ def PersistentPickler(persistent_id, *args, **kwargs): This covers the differences between Python 2 and 3 and PyPy/zodbpickle. """ p = Pickler(*args, **kwargs) - if sys.version_info[0] < 3: + if not PY3: p.inst_persistent_id = persistent_id # PyPy uses a python implementation of cPickle/zodbpickle in both Python 2 diff --git a/src/ZODB/blob.py b/src/ZODB/blob.py index e0d901703..4024a6d82 100644 --- a/src/ZODB/blob.py +++ b/src/ZODB/blob.py @@ -36,9 +36,10 @@ from ZODB._compat import decodebytes from ZODB._compat import ascii_bytes from ZODB._compat import INT_TYPES +from ZODB._compat import PY3 -if sys.version_info[0] >= 3: +if PY3: from io import FileIO as file diff --git a/src/ZODB/tests/IteratorStorage.py b/src/ZODB/tests/IteratorStorage.py index 761ca6c04..65113dc72 100644 --- a/src/ZODB/tests/IteratorStorage.py +++ b/src/ZODB/tests/IteratorStorage.py @@ -24,7 +24,6 @@ from transaction import Transaction -import sys import ZODB.blob try: @@ -159,10 +158,8 @@ def checkIterateWhileWriting(self): # We store another transaction with 1 object, the already running # iterator does not pick this up. self._dostore() - if sys.version_info[0] < 3: - self.assertRaises(StopIteration, iterator.next) - else: - self.assertRaises(StopIteration, iterator.__next__) + with self.assertRaises(StopIteration): + next(iterator) class ExtendedIteratorStorage(IteratorCompare): diff --git a/src/ZODB/tests/testCache.py b/src/ZODB/tests/testCache.py index 98cb08907..19c2c4b95 100644 --- a/src/ZODB/tests/testCache.py +++ b/src/ZODB/tests/testCache.py @@ -33,7 +33,6 @@ import ZODB.MappingStorage import ZODB.tests.util -PY2 = sys.version_info[0] == 2 class CacheTestBase(ZODB.tests.util.TestCase): diff --git a/src/ZODB/tests/testDB.py b/src/ZODB/tests/testDB.py index 0889b68e3..262ab6bfd 100644 --- a/src/ZODB/tests/testDB.py +++ b/src/ZODB/tests/testDB.py @@ -227,63 +227,62 @@ def open_convenience(): """ -if sys.version_info >= (2, 6): - def db_with_transaction(): - """Using databases with with +def db_with_transaction(): + """Using databases with with - The transaction method returns a context manager that when entered - starts a transaction with a private transaction manager. To - illustrate this, we start a trasnaction using a regular connection - and see that it isn't automatically committed or aborted as we use - the transaction context manager. + The transaction method returns a context manager that when entered + starts a transaction with a private transaction manager. To + illustrate this, we start a trasnaction using a regular connection + and see that it isn't automatically committed or aborted as we use + the transaction context manager. - >>> db = ZODB.tests.util.DB() - >>> conn = db.open() - >>> conn.root()['x'] = conn.root().__class__() - >>> transaction.commit() - >>> conn.root()['x']['x'] = 1 + >>> db = ZODB.tests.util.DB() + >>> conn = db.open() + >>> conn.root()['x'] = conn.root().__class__() + >>> transaction.commit() + >>> conn.root()['x']['x'] = 1 - >>> with db.transaction() as conn2: - ... conn2.root()['y'] = 1 + >>> with db.transaction() as conn2: + ... conn2.root()['y'] = 1 - >>> conn2.opened + >>> conn2.opened - Now, we'll open a 3rd connection a verify that +Now, we'll open a 3rd connection a verify that - >>> conn3 = db.open() - >>> conn3.root()['x'] - {} - >>> conn3.root()['y'] - 1 - >>> conn3.close() + >>> conn3 = db.open() + >>> conn3.root()['x'] + {} + >>> conn3.root()['y'] + 1 + >>> conn3.close() - Let's try again, but this time, we'll have an exception: +Let's try again, but this time, we'll have an exception: - >>> with db.transaction() as conn2: - ... conn2.root()['y'] = 2 - ... XXX #doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - NameError: name 'XXX' is not defined + >>> with db.transaction() as conn2: + ... conn2.root()['y'] = 2 + ... XXX #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + NameError: name 'XXX' is not defined - >>> conn2.opened + >>> conn2.opened - >>> conn3 = db.open() - >>> conn3.root()['x'] - {} - >>> conn3.root()['y'] - 1 - >>> conn3.close() + >>> conn3 = db.open() + >>> conn3.root()['x'] + {} + >>> conn3.root()['y'] + 1 + >>> conn3.close() - >>> transaction.commit() + >>> transaction.commit() - >>> conn3 = db.open() - >>> conn3.root()['x'] - {'x': 1} + >>> conn3 = db.open() + >>> conn3.root()['x'] + {'x': 1} - >>> db.close() - """ + >>> db.close() + """ def connection_allows_empty_version_for_idiots(): r""" diff --git a/src/ZODB/tests/testPersistentList.py b/src/ZODB/tests/testPersistentList.py index 6e6b47494..5bdfdfdbb 100644 --- a/src/ZODB/tests/testPersistentList.py +++ b/src/ZODB/tests/testPersistentList.py @@ -13,11 +13,11 @@ ############################################################################## """Test the list interface to PersistentList """ -import sys + import unittest from persistent.list import PersistentList -PY2 = sys.version_info[0] == 2 +from six import PY2 l0 = [] l1 = [0] @@ -84,7 +84,7 @@ def mycmp(a, b): except IndexError: pass else: - raise TestFailed("uu2[2] shouldn't be assignable") + self.fail("uu2[2] shouldn't be assignable") # Test __delitem__ @@ -95,7 +95,7 @@ def mycmp(a, b): except IndexError: pass else: - raise TestFailed("uu2[0] shouldn't be deletable") + self.fail("uu2[0] shouldn't be deletable") # Test __getslice__ @@ -191,7 +191,7 @@ def mycmp(a, b): except ValueError: pass else: - raise TestFailed("expected ValueError") + self.fail("expected ValueError") # Test reverse diff --git a/src/ZODB/tests/testPersistentMapping.py b/src/ZODB/tests/testPersistentMapping.py index ee8192de4..1be346e9f 100644 --- a/src/ZODB/tests/testPersistentMapping.py +++ b/src/ZODB/tests/testPersistentMapping.py @@ -23,20 +23,12 @@ import unittest import sys -import transaction from transaction import Transaction import ZODB from ZODB.MappingStorage import MappingStorage -from ZODB._compat import Unpickler -try: - import cStringIO -except ImportError: - # Py3 - import io as cStringIO - -PY2 = sys.version_info[0] == 2 +from six import PY2 # This pickle contains a persistent mapping pickle created from the # old code. @@ -68,35 +60,6 @@ def checkOldStyleRoot(self): self.assertTrue(hasattr(r, 'data')) self.assertTrue(not hasattr(r, '_container')) - # TODO: This test fails in ZODB 3.3a1. It's making some assumption(s) - # about pickles that aren't true. Hard to say when it stopped working, - # because this entire test suite hasn't been run for a long time, due to - # a mysterious "return None" at the start of the test_suite() function - # below. I noticed that when the new checkBackwardCompat() test wasn't - # getting run. - def TODO_checkNewPicklesAreSafe(self): - s = MappingStorage() - db = ZODB.DB(s) - r = db.open().root() - r[1] = 1 - r[2] = 2 - r[3] = r - transaction.commit() - # MappingStorage stores serialno + pickle in its _index. - root_pickle = s._index['\000' * 8][8:] - - # XXX not BytesIO really? - f = cStringIO.StringIO(root_pickle) - u = Unpickler(f) - klass_info = u.load() - klass = find_global(*klass_info[0]) - inst = klass.__new__(klass) - state = u.load() - inst.__setstate__(state) - - self.assertTrue(hasattr(inst, '_container')) - self.assertTrue(not hasattr(inst, 'data')) - def checkBackwardCompat(self): # Verify that the sanest of the ZODB 3.2 dotted paths still works. from persistent.mapping import PersistentMapping as newPath diff --git a/src/ZODB/tests/util.py b/src/ZODB/tests/util.py index b38b847ab..86d273446 100644 --- a/src/ZODB/tests/util.py +++ b/src/ZODB/tests/util.py @@ -19,7 +19,6 @@ import os import persistent import re -import sys import tempfile import time import transaction @@ -136,9 +135,6 @@ def testNothing(self): pass def assert_warning(category, func, warning_text=''): - if sys.version_info < (2, 6): - return func() # Can't use catch_warnings :( - with warnings.catch_warnings(record=True) as w: warnings.simplefilter('default') result = func() @@ -154,7 +150,7 @@ def assert_deprecated(func, warning_text=''): def wait(func=None, timeout=30): if func is None: return lambda f: wait(f, timeout) - for i in range(int(timeout*100)): + for _ in range(int(timeout*100)): if func(): return time.sleep(.01) diff --git a/src/ZODB/utils.py b/src/ZODB/utils.py index 404a558d9..f342924b7 100644 --- a/src/ZODB/utils.py +++ b/src/ZODB/utils.py @@ -17,7 +17,6 @@ import sys import time import threading -import warnings from binascii import hexlify, unhexlify from struct import pack, unpack from tempfile import mkstemp @@ -28,6 +27,8 @@ from ZODB._compat import BytesIO from ZODB._compat import ascii_bytes +from six import PY2 + __all__ = ['z64', 'p64', 'u64', @@ -40,38 +41,12 @@ 'tid_repr', 'positive_id', 'readable_tid_repr', - 'DEPRECATED_ARGUMENT', - 'deprecated37', - 'deprecated38', 'get_pickle_metadata', 'locked', ] -# A unique marker to give as the default value for a deprecated argument. -# The method should then do a -# -# if that_arg is not DEPRECATED_ARGUMENT: -# complain -# -# dance. -DEPRECATED_ARGUMENT = object() - -# Raise DeprecationWarning, noting that the deprecated thing will go -# away in ZODB 3.7. Point to the caller of our caller (i.e., at the -# code using the deprecated thing). -def deprecated37(msg): - warnings.warn("This will be removed in ZODB 3.7:\n%s" % msg, - DeprecationWarning, stacklevel=3) - -# Raise DeprecationWarning, noting that the deprecated thing will go -# away in ZODB 3.8. Point to the caller of our caller (i.e., at the -# code using the deprecated thing). -def deprecated38(msg): - warnings.warn("This will be removed in ZODB 3.8:\n%s" % msg, - DeprecationWarning, stacklevel=3) - -if sys.version_info[0] < 3: +if PY2: def as_bytes(obj): "Convert obj into bytes" return str(obj) @@ -185,7 +160,7 @@ def repr_to_oid(repr): # for 8-byte string tid b'\x03D\x14"\x94\x8bC\x99'. def readable_tid_repr(tid): result = tid_repr(tid) - if isinstance(tid, str) and len(tid) == 8: + if isinstance(tid, bytes) and len(tid) == 8: result = "%s %s" % (result, TimeStamp(tid)) return result @@ -316,7 +291,8 @@ def __call__(self, func): return Locked(func, preconditions=self.preconditions) -if os.environ.get('DEBUG_LOCKING'): +if os.environ.get('DEBUG_LOCKING'): # pragma: no cover + # NOTE: This only works on Python 3. class Lock: lock_class = threading.Lock