Commit 0c747419 authored by Jim Fulton's avatar Jim Fulton Committed by GitHub

Merge pull request #132 from zopefoundation/data-for-TransactionMetaData-and-TransactionRecord

Fixed: ZODB.Connection.TransactionMetaData didn't custom data
parents 9cfba5ed 930d15d6
......@@ -2,6 +2,9 @@
Change History
================
- Fixed: ``ZODB.Connection.TransactionMetaData`` didn't support custom data
storage that some storages rely on.
5.1.0 (2016-11-17)
==================
......
......@@ -28,14 +28,13 @@ from persistent.TimeStamp import TimeStamp
import ZODB.interfaces
from . import POSException, utils
from .Connection import TransactionMetaData
from .utils import z64, oid_repr, byte_ord, byte_chr, load_current
from .UndoLogCompatible import UndoLogCompatible
from ._compat import dumps, _protocol, py2_hasattr
log = logging.getLogger("ZODB.BaseStorage")
class BaseStorage(UndoLogCompatible):
"""Base class that supports storage implementations.
......@@ -359,25 +358,14 @@ def checkCurrentSerialInTransaction(self, oid, serial, transaction):
BaseStorage.checkCurrentSerialInTransaction = checkCurrentSerialInTransaction
@zope.interface.implementer(ZODB.interfaces.IStorageTransactionInformation)
class TransactionRecord(object):
class TransactionRecord(TransactionMetaData):
"""Abstract base class for iterator protocol"""
def __init__(self, tid, status, user, description, extension):
self.tid = tid
self.status = status
self.user = user
self.description = description
self.extension = extension
# XXX This is a workaround to make the TransactionRecord compatible with a
# transaction object because it is passed to tpc_begin().
def _ext_set(self, value):
self.extension = value
def _ext_get(self):
return self.extension
_extension = property(fset=_ext_set, fget=_ext_get)
TransactionMetaData.__init__(self, user, description, extension)
@zope.interface.implementer(ZODB.interfaces.IStorageRecordInformation)
class DataRecord(object):
......
......@@ -1321,3 +1321,17 @@ class TransactionMetaData(object):
@_extension.setter
def _extension(self, v):
self.extension = v
def data(self, ob):
try:
return self._data[id(ob)]
except (AttributeError, 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
......@@ -546,6 +546,28 @@ class IStorageTransactionMetaData(Interface):
extension = Attribute(
"A dictionary carrying a transaction's extended_info data")
def set_data(ob, data):
"""Hold data on behalf of an object
For objects such as storages 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.
The object passed should be the object that needs the data, as
opposed to simple object like a string. (Internally, the id of
the object is used as the key.)
"""
def data(ob):
"""Retrieve data held on behalf of an object.
See set_data.
"""
class IStorage(Interface):
"""A storage is responsible for storing and retrieving data of objects.
......
......@@ -87,6 +87,23 @@ class TransactionMetaDataTests(unittest.TestCase):
self.assertEqual(t.description, b'description\xc2\x80')
self.assertEqual(t.extension, dict(foo='FOO'))
def test_data(self):
t = TransactionMetaData()
# Can't get data that wasn't set:
with self.assertRaises(KeyError) as c:
t.data(self)
self.assertEqual(c.exception.args, (self,))
data = dict(a=1)
t.set_data(self, data)
self.assertEqual(t.data(self), data)
# Can't get something we haven't stored.
with self.assertRaises(KeyError) as c:
t.data(data)
self.assertEqual(c.exception.args, (data,))
def test_suite():
return unittest.makeSuite(TransactionMetaDataTests)
......
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