Skip to content

Commit

Permalink
Merge pull request #132 from zopefoundation/data-for-TransactionMetaD…
Browse files Browse the repository at this point in the history
…ata-and-TransactionRecord

Fixed: ZODB.Connection.TransactionMetaData didn't custom data
  • Loading branch information
jimfulton committed Nov 18, 2016
2 parents 9cfba5e + 930d15d commit 0c74741
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 15 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
==================

Expand Down
18 changes: 3 additions & 15 deletions src/ZODB/BaseStorage.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@

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.
Expand Down Expand Up @@ -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):
Expand Down
14 changes: 14 additions & 0 deletions src/ZODB/Connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1321,3 +1321,17 @@ def _extension(self):
@_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
22 changes: 22 additions & 0 deletions src/ZODB/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
17 changes: 17 additions & 0 deletions src/ZODB/tests/test_TransactionMetaData.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@ def tpc_begin(self, transaction):
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)

Expand Down

0 comments on commit 0c74741

Please sign in to comment.