Commit 29c01259 authored by Tim Peters's avatar Tim Peters

Officially deprecate Transaction.begin().

parent 02b5cc3c
...@@ -5,14 +5,14 @@ Release date: DD-MMM-YYYY ...@@ -5,14 +5,14 @@ Release date: DD-MMM-YYYY
Connection Connection
---------- ----------
ZODB intends to raise ConnnectionStateError if an attempt is made to ZODB intends to raise ConnnectionStateError if an attempt is made to close
close a connection while modifications are pending (the connection is a connection while modifications are pending (the connection is involved in
involved in a transaction that hasn't been abort()'ed or commit()'ed). a transaction that hasn't been ``abort()``'ed or ``commit()``'ed). It was
It was missing the case where the only pending modifications were made missing the case where the only pending modifications were made in
in subtransactions. This has been fixed. If an attempt to close a subtransactions. This has been fixed. If an attempt to close a connection
connection with pending subtransactions is made now, with pending subtransactions is made now::
``ConnnectionStateError: Cannot close a connection with a pending subtransaction`` ConnnectionStateError: Cannot close a connection with a pending subtransaction
is raised. is raised.
...@@ -29,21 +29,38 @@ transaction ...@@ -29,21 +29,38 @@ transaction
that ZODB intends to prevent committing a transaction in which a that ZODB intends to prevent committing a transaction in which a
ReadConflictError occurred; this was an obscure case it missed. ReadConflictError occurred; this was an obscure case it missed.
- Growing pains: ZODB 3.1 and 3.2 had a bug wherein ``Transaction.begin()`` - Growing pains: ZODB 3.2 had a bug wherein ``Transaction.begin()`` didn't
didn't abort the current transaction if the only pending changes were in a abort the current transaction if the only pending changes were in a
subtransaction. In ZODB 3.3, it's intended that transaction managers be subtransaction. In ZODB 3.3, it's intended that a transaction manager be
used instead of invoking methods directly on Transaction objects, and used to effect ``begin()`` (instead of invoking ``Transaction.begin()``),
calling ``begin()`` on a transaction manager didn't have this old bug. and calling ``begin()`` on a transaction manager didn't have this old
However, ``Transaction.begin()`` still exists in 3.3, and it had a worse bug. However, ``Transaction.begin()`` still exists in 3.3, and it had a
bug: it never aborted the transaction (not even if changes were pending worse bug: it never aborted the transaction (not even if changes were
outside of subtransactions). ``Transaction.begin()`` has been changed to pending outside of subtransactions). ``Transaction.begin()`` has been
abort the transaction, although it's still strongly recommended to invoke changed to abort the transaction. ``Transaction.begin()`` is also
``begin()`` on the relevant transaction manager instead. For example:: deprecated. Don't use it. Use ``begin()`` on the relevant transaction
manager instead. For example,
import transaction
transaction.begin() >>> import transaction
>>> txn = transaction.begin() # start a txn using the default TM
if using the default ThreadTransactionManager (see news for 3.3a3 below). if using the default ThreadTransactionManager (see news for 3.3a3 below).
In 3.3, it's intended that a single Transaction object is used for exactly
one transaction. So, unlike as in 3.2, when somtimes Transaction objects
were reused across transactions, but sometimes weren't, when you do
``Transaction.begin()`` in 3.3 a brand new transaction object is
created. That's why this use is deprecated. Code of the form:
>>> txn = transaction.get()
>>> ...
>>> txn.begin()
>>> ...
>>> txn.commit()
can't work as intended is 3.3, because ``txn`` is no longer the current
Transaction object the instant ``txn.begin()`` returns.
BTrees BTrees
------ ------
......
...@@ -386,38 +386,57 @@ class ZODBTests(unittest.TestCase): ...@@ -386,38 +386,57 @@ class ZODBTests(unittest.TestCase):
# transaction, and, in fact, when this test was written, # transaction, and, in fact, when this test was written,
# Transaction.begin() didn't do anything (everything from here # Transaction.begin() didn't do anything (everything from here
# down failed). # down failed).
cn = self._db.open()
rt = cn.root()
rt['a'] = 1
transaction.get().begin() # should abort adding 'a' to the root
rt = cn.root()
self.assertRaises(KeyError, rt.__getitem__, 'a')
# A longstanding bug: this didn't work if changes were only in
# subtransactions.
transaction.get().begin()
rt = cn.root()
rt['a'] = 2
transaction.get().commit(1)
transaction.get().begin() # Oh, bleech. Since Transaction.begin is also deprecated, we have
rt = cn.root() # to goof around suppressing the deprecation warning.
self.assertRaises(KeyError, rt.__getitem__, 'a') import warnings
# One more time, mixing "top level" and subtransaction changes. # First verify that Transaction.begin *is* deprecated, by turning
transaction.get().begin() # the warning into an error.
rt = cn.root() warnings.filterwarnings("error", category=DeprecationWarning)
rt['a'] = 3 self.assertRaises(DeprecationWarning, transaction.get().begin)
transaction.get().commit(1) del warnings.filters[0]
rt['b'] = 4
transaction.get().begin() # Now ignore DeprecationWarnings for the duration. Use a
rt = cn.root() # try/finally block to ensure we reenable DeprecationWarnings
self.assertRaises(KeyError, rt.__getitem__, 'a') # no matter what.
self.assertRaises(KeyError, rt.__getitem__, 'b') warnings.filterwarnings("ignore", category=DeprecationWarning)
try:
cn = self._db.open()
rt = cn.root()
rt['a'] = 1
transaction.get().begin() # should abort adding 'a' to the root
rt = cn.root()
self.assertRaises(KeyError, rt.__getitem__, 'a')
# A longstanding bug: this didn't work if changes were only in
# subtransactions.
transaction.get().begin()
rt = cn.root()
rt['a'] = 2
transaction.get().commit(1)
transaction.get().begin()
rt = cn.root()
self.assertRaises(KeyError, rt.__getitem__, 'a')
# One more time, mixing "top level" and subtransaction changes.
transaction.get().begin()
rt = cn.root()
rt['a'] = 3
transaction.get().commit(1)
rt['b'] = 4
transaction.get().begin()
rt = cn.root()
self.assertRaises(KeyError, rt.__getitem__, 'a')
self.assertRaises(KeyError, rt.__getitem__, 'b')
cn.close()
cn.close() finally:
del warnings.filters[0]
def test_suite(): def test_suite():
return unittest.makeSuite(ZODBTests, 'check') return unittest.makeSuite(ZODBTests, 'check')
......
...@@ -136,6 +136,7 @@ XXX This code isn't tested. ...@@ -136,6 +136,7 @@ XXX This code isn't tested.
import logging import logging
import sys import sys
import thread import thread
import warnings
_marker = object() _marker = object()
...@@ -230,6 +231,9 @@ class Transaction(object): ...@@ -230,6 +231,9 @@ class Transaction(object):
self._resources.append(adapter) self._resources.append(adapter)
def begin(self): def begin(self):
warnings.warn("Transaction.begin() should no longer be used; use "
"the begin() method of a transaction manager.",
DeprecationWarning)
if (self._resources or if (self._resources or
self._sub or self._sub or
self._nonsub or self._nonsub or
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment