Skip to content

Commit

Permalink
Remove ITransactionDeprecated and supporting code.
Browse files Browse the repository at this point in the history
Fixes #89.

Has just enough documentation fixes from #88 to make the doctests pass on current versions of sphinx/docutils.
  • Loading branch information
jamadden committed Dec 11, 2019
1 parent a47b6c7 commit d501419
Show file tree
Hide file tree
Showing 6 changed files with 12 additions and 664 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Expand Up @@ -9,6 +9,11 @@

- Add support for Python 3.8.

- Drop support for legacy transaction APIs including
``Transaction.register()`` and old ZODB3-style datamanagers. See
`issue 89
<https://github.com/zopefoundation/transaction/issues/89>`_.

- ``TransactionManager.run`` now commits/aborts the transaction
"active" after the execution of *func* (and no longer the initial
transaction which might already have been committed/aborted by *func*)
Expand Down
5 changes: 0 additions & 5 deletions transaction/_compat.py
Expand Up @@ -10,11 +10,6 @@
# py2
text_type = unicode

def bytes_(s, encoding='latin-1', errors='strict'):
if isinstance(s, text_type): # pragma: no cover
s = s.encode(encoding, errors)
return s

def text_(s):
if not isinstance(s, text_type): # pragma: no cover
s = s.decode('utf-8')
Expand Down
181 changes: 4 additions & 177 deletions transaction/_transaction.py
Expand Up @@ -11,7 +11,6 @@
# FOR A PARTICULAR PURPOSE.
#
############################################################################
import binascii
import logging
import sys
import warnings
Expand All @@ -25,8 +24,6 @@
from transaction import interfaces
from transaction._compat import reraise
from transaction._compat import get_thread_ident
from transaction._compat import native_
from transaction._compat import bytes_
from transaction._compat import StringIO
from transaction._compat import text_type

Expand All @@ -45,11 +42,6 @@ def _makeLogger(): #pragma NO COVER
return logging.getLogger("txn.%d" % get_thread_ident())


# The point of this is to avoid hiding exceptions (which the builtin
# hasattr() does).
def myhasattr(obj, attr):
return getattr(obj, attr, _marker) is not _marker

class Status(object):
# ACTIVE is the initial state.
ACTIVE = "Active"
Expand All @@ -69,8 +61,7 @@ class _NoSynchronizers(object):
def map(_f):
"Does nothing"

@implementer(interfaces.ITransaction,
interfaces.ITransactionDeprecated)
@implementer(interfaces.ITransaction)
class Transaction(object):


Expand Down Expand Up @@ -197,12 +188,6 @@ def join(self, resource):
# I think some users want it.
raise ValueError("expected txn status %r or %r, but it's %r" % (
Status.ACTIVE, Status.DOOMED, self.status))
# TODO: the prepare check is a bit of a hack, perhaps it would
# be better to use interfaces. If this is a ZODB4-style
# resource manager, it needs to be adapted, too.
if myhasattr(resource, "prepare"):
# TODO: deprecate 3.6
resource = DataManagerAdapter(resource)
self._resources.append(resource)

if self._savepoint2index:
Expand Down Expand Up @@ -265,33 +250,6 @@ def _invalidate_all_savepoints(self):
savepoint.transaction = None # invalidate
self._savepoint2index.clear()


def register(self, obj):
""" See ITransaction.
"""
# The old way of registering transaction participants.
#
# register() is passed either a persistent object or a
# resource manager like the ones defined in ZODB.DB.
# If it is passed a persistent object, that object should
# be stored when the transaction commits. For other
# objects, the object implements the standard two-phase
# commit protocol.
manager = getattr(obj, "_p_jar", obj)
if manager is None:
raise ValueError("Register with no manager")
adapter = self._adapters.get(manager)
if adapter is None:
adapter = MultiObjectResourceAdapter(manager)
adapter.objects.append(obj)
self._adapters[manager] = adapter
self.join(adapter)
else:
# TODO: comment out this expensive assert later
# Use id() to guard against proxies.
assert id(obj) not in map(id, adapter.objects)
adapter.objects.append(obj)

def commit(self):
""" See ITransaction.
"""
Expand Down Expand Up @@ -649,145 +607,14 @@ def isRetryableError(self, error):
# TODO: We need a better name for the adapters.


class MultiObjectResourceAdapter(object):
"""Adapt the old-style register() call to the new-style join().
With join(), a resource manager like a Connection registers with
the transaction manager. With register(), an individual object
is passed to register().
"""
def __init__(self, jar):
self.manager = jar
self.objects = []
self.ncommitted = 0

def __repr__(self):
return "<%s for %s at %s>" % (self.__class__.__name__,
self.manager, id(self))

def sortKey(self):
return self.manager.sortKey()

def tpc_begin(self, txn):
self.manager.tpc_begin(txn)

def tpc_finish(self, txn):
self.manager.tpc_finish(txn)

def tpc_abort(self, txn):
self.manager.tpc_abort(txn)

def commit(self, txn):
for o in self.objects:
self.manager.commit(o, txn)
self.ncommitted += 1

def tpc_vote(self, txn):
self.manager.tpc_vote(txn)

def abort(self, txn):
t = None
v = None
tb = None
try:
for o in self.objects:
try:
self.manager.abort(o, txn)
except:
# Capture the first exception and re-raise it after
# aborting all the other objects.
if tb is None:
t, v, tb = sys.exc_info()
txn.log.error("Failed to abort object: %s",
object_hint(o), exc_info=sys.exc_info())

if tb is not None:
reraise(t, v, tb)
finally:
del t, v, tb


def rm_key(rm):
func = getattr(rm, 'sortKey', None)
if func is not None:
return func()

def object_hint(o):
"""Return a string describing the object.
This function does not raise an exception.
"""
# We should always be able to get __class__.
klass = o.__class__.__name__
# oid would be great, but maybe this isn't a persistent object.
oid = getattr(o, "_p_oid", _marker)
if oid is not _marker:
oid = oid_repr(oid)
else:
oid = 'None'
return "%s oid=%s" % (klass, oid)

def oid_repr(oid):
if isinstance(oid, str) and len(oid) == 8:
# Convert to hex and strip leading zeroes.
as_hex = native_(
binascii.hexlify(bytes_(oid, 'ascii')), 'ascii').lstrip('0')
# Ensure two characters per input byte.
if len(as_hex) & 1:
as_hex = '0' + as_hex
elif as_hex == '':
as_hex = '00'
return '0x' + as_hex
else:
return repr(oid)


# TODO: deprecate for 3.6.
class DataManagerAdapter(object):
"""Adapt zodb 4-style data managers to zodb3 style
Adapt transaction.interfaces.IDataManager to
ZODB.interfaces.IPureDatamanager
"""

# Note that it is pretty important that this does not have a _p_jar
# attribute. This object will be registered with a zodb3 TM, which
# will then try to get a _p_jar from it, using it as the default.
# (Objects without a _p_jar are their own data managers.)

def __init__(self, datamanager):
self._datamanager = datamanager

# TODO: I'm not sure why commit() doesn't do anything

def commit(self, transaction):
# We don't do anything here because ZODB4-style data managers
# didn't have a separate commit step
pass

def abort(self, transaction):
self._datamanager.abort(transaction)

def tpc_begin(self, transaction):
# We don't do anything here because ZODB4-style data managers
# didn't have a separate tpc_begin step
pass

def tpc_abort(self, transaction):
self._datamanager.abort(transaction)

def tpc_finish(self, transaction):
self._datamanager.commit(transaction)

def tpc_vote(self, transaction):
self._datamanager.prepare(transaction)

def sortKey(self):
return self._datamanager.sortKey()


@implementer(interfaces.ISavepoint)
class Savepoint:
class Savepoint(object):
"""Transaction savepoint.
Transaction savepoints coordinate savepoints for data managers
Expand Down Expand Up @@ -831,7 +658,7 @@ def rollback(self):
transaction._saveAndRaiseCommitishError() # reraises!


class AbortSavepoint:
class AbortSavepoint(object):

def __init__(self, datamanager, transaction):
self.datamanager = datamanager
Expand All @@ -842,7 +669,7 @@ def rollback(self):
self.transaction._unjoin(self.datamanager)


class NoRollbackSavepoint:
class NoRollbackSavepoint(object):

def __init__(self, datamanager):
self.datamanager = datamanager
Expand Down
14 changes: 0 additions & 14 deletions transaction/interfaces.py
Expand Up @@ -431,20 +431,6 @@ def isRetryableError(error):
issues in the underlying storage engine.
"""

class ITransactionDeprecated(Interface):
"""Deprecated parts of the transaction API."""

def begin(info=None):
"""Begin a new transaction.
If the transaction is in progress, it is aborted and a new
transaction is started using the same transaction object.
"""

# TODO: deprecate this for 3.6.
def register(object):
"""Register the given object for transaction control."""


class IDataManager(Interface):
"""Objects that manage transactional storage.
Expand Down

0 comments on commit d501419

Please sign in to comment.