Commit a9c8bcfe authored by Jim Fulton's avatar Jim Fulton

New transaction API for storing data on behalf of objects

For objects such as data managers or their subobjects that
work with multiple transactions, it's convenient to store
transaction-specific data on the transaction itself.  The
transaction knows nothing about the data, but simply holds it
on behalf of the object.

Discussion:
https://groups.google.com/forum/#!topic/python-transaction/oUzj3uIHBgA
parent 86a18280
...@@ -4,6 +4,9 @@ Changes ...@@ -4,6 +4,9 @@ Changes
1.6.0 (TBD) 1.6.0 (TBD)
----------- -----------
- New transaction API for storing data on behalf of objects, such as
data managers.
- Drop references to data managers joined to a transaction when it is - Drop references to data managers joined to a transaction when it is
committed or aborted. committed or aborted.
......
...@@ -441,8 +441,29 @@ class Transaction(object): ...@@ -441,8 +441,29 @@ class Transaction(object):
if self._manager: if self._manager:
self._manager.free(self) self._manager.free(self)
if hasattr(self, '_data'):
delattr(self, '_data')
del self._resources[:] del self._resources[:]
def data(self, ob):
try:
data = self._data
except AttributeError:
raise KeyError(ob)
try:
return data[id(ob)]
except KeyError:
raise KeyError(ob)
def set_data(self, ob, ob_data):
try:
data = self._data
except AttributeError:
data = self._data = {}
data[id(ob)] = ob_data
def abort(self): def abort(self):
""" See ITransaction. """ See ITransaction.
...@@ -497,6 +518,7 @@ class Transaction(object): ...@@ -497,6 +518,7 @@ class Transaction(object):
""" """
self._extension[name] = value self._extension[name] = value
# TODO: We need a better name for the adapters. # TODO: We need a better name for the adapters.
......
...@@ -298,6 +298,22 @@ class ITransaction(Interface): ...@@ -298,6 +298,22 @@ class ITransaction(Interface):
by a top-level transaction commit. by a top-level transaction commit.
""" """
def set_data(self, object, data):
"""Hold data on behalf of an object
For objects such as data managers or their subobjects that
work with multiple transactions, it's convenient to store
transaction-specific data on the transaction itself. The
transaction knows nothing about the data, but simply holds it
on behalf of the object.
"""
def data(self, object):
"""Retrieve data held on behalf of an object.
See set_data.
"""
class ITransactionDeprecated(Interface): class ITransactionDeprecated(Interface):
"""Deprecated parts of the transaction API.""" """Deprecated parts of the transaction API."""
......
...@@ -1001,6 +1001,29 @@ class TransactionTests(unittest.TestCase): ...@@ -1001,6 +1001,29 @@ class TransactionTests(unittest.TestCase):
txn.setExtendedInfo('frob', 'quxxxx') txn.setExtendedInfo('frob', 'quxxxx')
self.assertEqual(txn._extension, {'frob': 'quxxxx', 'baz': 'spam'}) self.assertEqual(txn._extension, {'frob': 'quxxxx', 'baz': 'spam'})
def test_data(self):
txn = self._makeOne()
# Can't get data that wasn't set:
with self.assertRaises(KeyError) as c:
txn.data(self)
self.assertEqual(c.exception.args, (self,))
data = dict(a=1)
txn.set_data(self, data)
self.assertEqual(txn.data(self), data)
# Can't get something we haven't stored.
with self.assertRaises(KeyError) as c:
txn.data(data)
self.assertEqual(c.exception.args, (data,))
# When the transaction ends, data are discarded:
txn.commit()
with self.assertRaises(KeyError) as c:
txn.data(self)
self.assertEqual(c.exception.args, (self,))
class MultiObjectResourceAdapterTests(unittest.TestCase): class MultiObjectResourceAdapterTests(unittest.TestCase):
......
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