transaction-1.4.3/0000775000175000017500000000000012312641352013751 5ustar tseavertseavertransaction-1.4.3/setup.cfg0000664000175000017500000000037712312641352015601 0ustar tseavertseaver[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 [nosetests] cover-package = transaction nocapture = 1 where = transaction cover-erase = 1 [aliases] dev = develop easy_install transaction[testing] docs = develop easy_install transaction[docs] transaction-1.4.3/buildout.cfg0000664000175000017500000000013512073316126016262 0ustar tseavertseaver[buildout] develop = . parts = test [test] recipe = zc.recipe.testrunner eggs = transaction transaction-1.4.3/PKG-INFO0000664000175000017500000001621712312641352015055 0ustar tseavertseaverMetadata-Version: 1.0 Name: transaction Version: 1.4.3 Summary: Transaction management for Python Home-page: http://www.zope.org/Products/ZODB Author: Zope Corporation Author-email: zodb-dev@zope.org License: ZPL 2.1 Description: ============ Transactions ============ This package contains a generic transaction implementation for Python. It is mainly used by the ZODB. See http://zodb.readthedocs.org/en/latest/transactions.html for narrative documentation on its usage. Changes ======= 1.4.2 (2014-03-20) ------------------ - Added support for Python 3.4. 1.4.1 (2013-02-20) ------------------ - Document that values returned by ``sortKey`` must be strings, in order to guarantee total ordering. - Fix occasional RuntimeError: dictionary changed size during iteration errors in transaction.weakset on Python 3. 1.4.0 (2013-01-03) ------------------ - Updated Trove classifiers. 1.4.0b1 (2012-12-18) -------------------- - Converted existing doctests into Sphinx documentation (snippets are exercised via 'tox'). - 100% unit test coverage. - Backward incompatibility: raise ValueError rather than AssertionError for runtime errors: - In ``Transaction.doom`` if the transaction is in a non-doomable state. - In ``TransactionManager.attempts`` if passed a non-positive value. - In ``TransactionManager.free`` if passed a foreign transaction. - Declared support for Python 3.3 in ``setup.py``, and added ``tox`` testing. - When a non-retryable exception was raised as the result of a call to ``transaction.manager.commit`` within the "attempts" machinery, the exception was not reraised properly. Symptom: an unrecoverable exception such as ``Unsupported: Storing blobs in is not supported.`` would be swallowed inappropriately. 1.3.0 (2012-05-16) ------------------ - Added Sphinx API docuementation. - Added explicit support for PyPy. - Dropped use of Python3-impatible ``zope.interface.implements`` class advisor in favor of ``zope.interface.implementer`` class decorator. - Added support for continuous integration using ``tox`` and ``jenkins``. - Added ``setup.py docs`` alias (installs ``Sphinx`` and dependencies). - Added ``setup.py dev`` alias (runs ``setup.py develop`` plus installs ``nose`` and ``coverage``). - Python 3.3 compatibility. - Fix "for attempt in transaction.attempts(x)" machinery, which would not retry a transaction if its implicit call to ``.commit()`` itself raised a transient error. Symptom: seeing conflict errors even though you thought you were retrying some number of times via the "attempts" machinery (the first attempt to generate an exception during commit would cause that exception to be raised). 1.2.0 (2011-12-05) ------------------ New Features: - Python 3.2 compatibility. - Dropped Python 2.4 and 2.5 compatibility (use 1.1.1 if you need to use "transaction" under these Python versions). 1.1.1 (2010-09-16) ------------------ Bug Fixes: - Code in ``_transaction.py`` held on to local references to traceback objects after calling ``sys.exc_info()`` to get one, causing potential reference leakages. - Fixed ``hexlify`` NameError in ``transaction._transaction.oid_repr`` and add test. 1.1.0 (1010-05-12) ------------------ New Features: - Transaction managers and the transaction module can be used with the with statement to define transaction boundaries, as in:: with transaction: ... do some things ... See transaction/tests/convenience.txt for more details. - There is a new iterator function that automates dealing with transient errors (such as ZODB confict errors). For example, in:: for attempt in transaction.attempts(5): with attempt: ... do some things .. If the work being done raises transient errors, the transaction will be retried up to 5 times. See transaction/tests/convenience.txt for more details. Bugs fixed: - Fixed a bug that caused extra commit calls to be made on data managers under certain special circumstances. https://mail.zope.org/pipermail/zodb-dev/2010-May/013329.html - When threads were reused, transaction data could leak accross them, causing subtle application bugs. https://bugs.launchpad.net/zodb/+bug/239086 1.0.1 (2010-05-07) ------------------ - LP #142464: remove double newline between log entries: it makes doing smarter formatting harder. - Updated tests to remove use of deprecated ``zope.testing.doctest``. 1.0.0 (2009-07-24) ------------------ - Fix test that incorrectly relied on the order of a list that was generated from a dict. - Remove crufty DEPENDENCIES.cfg left over from zpkg. 1.0a1 (2007-12-18) ------------------ = Initial release, branched from ZODB trunk on 2007-11-08 (aka "3.9.0dev"). - Remove (deprecated) support for beforeCommitHook alias to addBeforeCommitHook. - Add weakset tests. - Remove unit tests that depend on ZODB.tests.utils from test_transaction (these are actually integration tests). Platform: any Classifier: Development Status :: 6 - Mature Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Topic :: Database Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: Unix Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Framework :: ZODB transaction-1.4.3/tox.ini0000664000175000017500000000140512312641051015260 0ustar tseavertseaver[tox] envlist = # Jython support pending 2.7 support, due 2012-07-15 or so. See: # http://fwierzbicki.blogspot.com/2012/03/adconion-to-fund-jython-27.html # py26,py27,py32,pypy,jython,coverage py26,py27,py32,py33,py34,pypy,coverage,docs [testenv] commands = python setup.py test -q deps = transaction [testenv:jython] commands = jython setup.py test -q [testenv:coverage] basepython = python2.6 commands = nosetests --with-xunit --with-xcoverage deps = nose coverage nosexcover [testenv:docs] basepython = python2.6 commands = sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest deps = Sphinx repoze.sphinx.autointerface transaction-1.4.3/.gitignore0000664000175000017500000000017212074035754015751 0ustar tseavertseaver*.pyc *.so __pycache__ build docs/_build bin develop-eggs eggs parts .tox .coverage nosetests.xml coverage.xml *.egg-info transaction-1.4.3/setup.py0000664000175000017500000000474012312641266015474 0ustar tseavertseaver############################################################################## # # Copyright (c) 2007 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Setup """ import os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = (open(os.path.join(here, 'README.rst')).read() + '\n\n' + open(os.path.join(here, 'CHANGES.rst')).read()) setup(name='transaction', version='1.4.3', description='Transaction management for Python', long_description=README, classifiers=[ "Development Status :: 6 - Mature", "License :: OSI Approved :: Zope Public License", "Programming Language :: Python", "Topic :: Database", "Topic :: Software Development :: Libraries :: Python Modules", "Operating System :: Microsoft :: Windows", "Operating System :: Unix", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Framework :: ZODB", ], author="Zope Corporation", author_email="zodb-dev@zope.org", url="http://www.zope.org/Products/ZODB", license="ZPL 2.1", platforms=["any"], packages=find_packages(), include_package_data=True, zip_safe=False, test_suite="transaction.tests", tests_require = [ 'zope.interface', ], install_requires=[ 'zope.interface', ], extras_require = { 'docs': ['Sphinx', 'repoze.sphinx.autointerface'], 'testing': ['nose', 'coverage'], }, entry_points = """\ """ ) transaction-1.4.3/bootstrap.py0000664000175000017500000002443512312641051016344 0ustar tseavertseaver############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Bootstrap a buildout-based project Simply run this script in a directory containing a buildout.cfg. The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. """ import os, shutil, sys, tempfile, urllib, urllib2, subprocess from optparse import OptionParser if sys.platform == 'win32': def quote(c): if ' ' in c: return '"%s"' % c # work around spawn lamosity on windows else: return c else: quote = str # See zc.buildout.easy_install._has_broken_dash_S for motivation and comments. stdout, stderr = subprocess.Popen( [sys.executable, '-Sc', 'try:\n' ' import ConfigParser\n' 'except ImportError:\n' ' print 1\n' 'else:\n' ' print 0\n'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() has_broken_dash_S = bool(int(stdout.strip())) # In order to be more robust in the face of system Pythons, we want to # run without site-packages loaded. This is somewhat tricky, in # particular because Python 2.6's distutils imports site, so starting # with the -S flag is not sufficient. However, we'll start with that: if not has_broken_dash_S and 'site' in sys.modules: # We will restart with python -S. args = sys.argv[:] args[0:0] = [sys.executable, '-S'] args = map(quote, args) os.execv(sys.executable, args) # Now we are running with -S. We'll get the clean sys.path, import site # because distutils will do it later, and then reset the path and clean # out any namespace packages from site-packages that might have been # loaded by .pth files. clean_path = sys.path[:] import site # imported because of its side effects sys.path[:] = clean_path for k, v in sys.modules.items(): if k in ('setuptools', 'pkg_resources') or ( hasattr(v, '__path__') and len(v.__path__) == 1 and not os.path.exists(os.path.join(v.__path__[0], '__init__.py'))): # This is a namespace package. Remove it. sys.modules.pop(k) is_jython = sys.platform.startswith('java') setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py' distribute_source = 'http://python-distribute.org/distribute_setup.py' # parsing arguments def normalize_to_url(option, opt_str, value, parser): if value: if '://' not in value: # It doesn't smell like a URL. value = 'file://%s' % ( urllib.pathname2url( os.path.abspath(os.path.expanduser(value))),) if opt_str == '--download-base' and not value.endswith('/'): # Download base needs a trailing slash to make the world happy. value += '/' else: value = None name = opt_str[2:].replace('-', '_') setattr(parser.values, name, value) usage = '''\ [DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options] Bootstraps a buildout-based project. Simply run this script in a directory containing a buildout.cfg, using the Python that you want bin/buildout to use. Note that by using --setup-source and --download-base to point to local resources, you can keep this script from going over the network. ''' parser = OptionParser(usage=usage) parser.add_option("-v", "--version", dest="version", help="use a specific zc.buildout version") parser.add_option("-d", "--distribute", action="store_true", dest="use_distribute", default=False, help="Use Distribute rather than Setuptools.") parser.add_option("--setup-source", action="callback", dest="setup_source", callback=normalize_to_url, nargs=1, type="string", help=("Specify a URL or file location for the setup file. " "If you use Setuptools, this will default to " + setuptools_source + "; if you use Distribute, this " "will default to " + distribute_source + ".")) parser.add_option("--download-base", action="callback", dest="download_base", callback=normalize_to_url, nargs=1, type="string", help=("Specify a URL or directory for downloading " "zc.buildout and either Setuptools or Distribute. " "Defaults to PyPI.")) parser.add_option("--eggs", help=("Specify a directory for storing eggs. Defaults to " "a temporary directory that is deleted when the " "bootstrap script completes.")) parser.add_option("-t", "--accept-buildout-test-releases", dest='accept_buildout_test_releases', action="store_true", default=False, help=("Normally, if you do not specify a --version, the " "bootstrap script and buildout gets the newest " "*final* versions of zc.buildout and its recipes and " "extensions for you. If you use this flag, " "bootstrap and buildout will get the newest releases " "even if they are alphas or betas.")) parser.add_option("-c", None, action="store", dest="config_file", help=("Specify the path to the buildout configuration " "file to be used.")) options, args = parser.parse_args() if options.eggs: eggs_dir = os.path.abspath(os.path.expanduser(options.eggs)) else: eggs_dir = tempfile.mkdtemp() if options.setup_source is None: if options.use_distribute: options.setup_source = distribute_source else: options.setup_source = setuptools_source if options.accept_buildout_test_releases: args.insert(0, 'buildout:accept-buildout-test-releases=true') try: import pkg_resources import setuptools # A flag. Sometimes pkg_resources is installed alone. if not hasattr(pkg_resources, '_distribute'): raise ImportError except ImportError: ez_code = urllib2.urlopen( options.setup_source).read().replace('\r\n', '\n') ez = {} exec ez_code in ez setup_args = dict(to_dir=eggs_dir, download_delay=0) if options.download_base: setup_args['download_base'] = options.download_base if options.use_distribute: setup_args['no_fake'] = True if sys.version_info[:2] == (2, 4): setup_args['version'] = '0.6.32' ez['use_setuptools'](**setup_args) if 'pkg_resources' in sys.modules: reload(sys.modules['pkg_resources']) import pkg_resources # This does not (always?) update the default working set. We will # do it. for path in sys.path: if path not in pkg_resources.working_set.entries: pkg_resources.working_set.add_entry(path) cmd = [quote(sys.executable), '-c', quote('from setuptools.command.easy_install import main; main()'), '-mqNxd', quote(eggs_dir)] if not has_broken_dash_S: cmd.insert(1, '-S') find_links = options.download_base if not find_links: find_links = os.environ.get('bootstrap-testing-find-links') if not find_links and options.accept_buildout_test_releases: find_links = 'http://downloads.buildout.org/' if find_links: cmd.extend(['-f', quote(find_links)]) if options.use_distribute: setup_requirement = 'distribute' else: setup_requirement = 'setuptools' ws = pkg_resources.working_set setup_requirement_path = ws.find( pkg_resources.Requirement.parse(setup_requirement)).location env = dict( os.environ, PYTHONPATH=setup_requirement_path) requirement = 'zc.buildout' version = options.version if version is None and not options.accept_buildout_test_releases: # Figure out the most recent final version of zc.buildout. import setuptools.package_index _final_parts = '*final-', '*final' def _final_version(parsed_version): for part in parsed_version: if (part[:1] == '*') and (part not in _final_parts): return False return True index = setuptools.package_index.PackageIndex( search_path=[setup_requirement_path]) if find_links: index.add_find_links((find_links,)) req = pkg_resources.Requirement.parse(requirement) if index.obtain(req) is not None: best = [] bestv = None for dist in index[req.project_name]: distv = dist.parsed_version if distv >= pkg_resources.parse_version('2dev'): continue if _final_version(distv): if bestv is None or distv > bestv: best = [dist] bestv = distv elif distv == bestv: best.append(dist) if best: best.sort() version = best[-1].version if version: requirement += '=='+version else: requirement += '<2dev' cmd.append(requirement) if is_jython: import subprocess exitcode = subprocess.Popen(cmd, env=env).wait() else: # Windows prefers this, apparently; otherwise we would prefer subprocess exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env])) if exitcode != 0: sys.stdout.flush() sys.stderr.flush() print ("An error occurred when trying to install zc.buildout. " "Look above this message for any errors that " "were output by easy_install.") sys.exit(exitcode) ws.add_entry(eggs_dir) ws.require(requirement) import zc.buildout.buildout # If there isn't already a command in the args, add bootstrap if not [a for a in args if '=' not in a]: args.append('bootstrap') # if -c was provided, we push it back into args for buildout's main function if options.config_file is not None: args[0:0] = ['-c', options.config_file] zc.buildout.buildout.main(args) if not options.eggs: # clean up temporary egg directory shutil.rmtree(eggs_dir) transaction-1.4.3/transaction/0000775000175000017500000000000012312641352016276 5ustar tseavertseavertransaction-1.4.3/transaction/_compat.py0000664000175000017500000000440112073316126020273 0ustar tseavertseaverimport sys import types PY3 = sys.version_info[0] == 3 if PY3: # pragma: no cover string_types = str, integer_types = int, class_types = type, text_type = str binary_type = bytes long = int else: string_types = basestring, integer_types = (int, long) class_types = (type, types.ClassType) text_type = unicode binary_type = str long = long def bytes_(s, encoding='latin-1', errors='strict'): #pragma NO COVER if isinstance(s, text_type): return s.encode(encoding, errors) return s if PY3: # pragma: no cover def native_(s, encoding='latin-1', errors='strict'): #pragma NO COVER if isinstance(s, text_type): return s return str(s, encoding, errors) else: def native_(s, encoding='latin-1', errors='strict'): #pragma NO COVER if isinstance(s, text_type): return s.encode(encoding, errors) return str(s) if PY3: #pragma NO COVER from io import StringIO else: from io import BytesIO as StringIO if PY3: #pragma NO COVER from collections import MutableMapping else: from UserDict import UserDict as MutableMapping if PY3: # pragma: no cover import builtins exec_ = getattr(builtins, "exec") def reraise(tp, value, tb=None): #pragma NO COVER if value.__traceback__ is not tb: raise value.with_traceback(tb) raise value else: # pragma: no cover def exec_(code, globs=None, locs=None): #pragma NO COVER """Execute code in a namespace.""" if globs is None: frame = sys._getframe(1) globs = frame.f_globals if locs is None: locs = frame.f_locals del frame elif locs is None: locs = globs exec("""exec code in globs, locs""") exec_("""def reraise(tp, value, tb=None): raise tp, value, tb """) if PY3: #pragma NO COVER try: from threading import get_ident as get_thread_ident except ImportError: from threading import _get_ident as get_thread_ident else: from thread import get_ident as get_thread_ident if PY3: def func_name(func): #pragma NO COVER return func.__name__ else: def func_name(func): #pragma NO COVER return func.func_name transaction-1.4.3/transaction/__init__.py0000664000175000017500000000260312073316126020412 0ustar tseavertseaver############################################################################ # # Copyright (c) 2001, 2002, 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################ """Exported transaction functions. $Id$ """ from transaction._transaction import Transaction from transaction._manager import TransactionManager from transaction._manager import ThreadTransactionManager # NB: "with transaction:" does not work under Python 3 because they worked # really hard to break looking up special methods like __enter__ and __exit__ # via getattr and getattribute; see http://bugs.python.org/issue12022. On # Python 3, you must use ``with transaction.manager`` instead. manager = ThreadTransactionManager() get = __enter__ = manager.get begin = manager.begin commit = manager.commit abort = manager.abort __exit__ = manager.__exit__ doom = manager.doom isDoomed = manager.isDoomed savepoint = manager.savepoint attempts = manager.attempts transaction-1.4.3/transaction/_manager.py0000664000175000017500000001274012073316126020427 0ustar tseavertseaver############################################################################ # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################ """A TransactionManager controls transaction boundaries. It coordinates application code and resource managers, so that they are associated with the right transaction. """ import sys import threading from zope.interface import implementer from transaction.interfaces import ITransactionManager from transaction.interfaces import TransientError from transaction.weakset import WeakSet from transaction._compat import reraise from transaction._transaction import Transaction # We have to remember sets of synch objects, especially Connections. # But we don't want mere registration with a transaction manager to # keep a synch object alive forever; in particular, it's common # practice not to explicitly close Connection objects, and keeping # a Connection alive keeps a potentially huge number of other objects # alive (e.g., the cache, and everything reachable from it too). # Therefore we use "weak sets" internally. # Call the ISynchronizer newTransaction() method on every element of # WeakSet synchs. # A transaction manager needs to do this whenever begin() is called. # Since it would be good if tm.get() returned the new transaction while # newTransaction() is running, calling this has to be delayed until after # the transaction manager has done whatever it needs to do to make its # get() return the new txn. def _new_transaction(txn, synchs): if synchs: synchs.map(lambda s: s.newTransaction(txn)) # Important: we must always pass a WeakSet (even if empty) to the Transaction # constructor: synchronizers are registered with the TM, but the # ISynchronizer xyzCompletion() methods are called by Transactions without # consulting the TM, so we need to pass a mutable collection of synchronizers # so that Transactions "see" synchronizers that get registered after the # Transaction object is constructed. @implementer(ITransactionManager) class TransactionManager(object): def __init__(self): self._txn = None self._synchs = WeakSet() def begin(self): """ See ITransactionManager. """ if self._txn is not None: self._txn.abort() txn = self._txn = Transaction(self._synchs, self) _new_transaction(txn, self._synchs) return txn __enter__ = lambda self: self.begin() def get(self): """ See ITransactionManager. """ if self._txn is None: self._txn = Transaction(self._synchs, self) return self._txn def free(self, txn): if txn is not self._txn: raise ValueError("Foreign transaction") self._txn = None def registerSynch(self, synch): """ See ITransactionManager. """ self._synchs.add(synch) def unregisterSynch(self, synch): """ See ITransactionManager. """ self._synchs.remove(synch) def isDoomed(self): """ See ITransactionManager. """ return self.get().isDoomed() def doom(self): """ See ITransactionManager. """ return self.get().doom() def commit(self): """ See ITransactionManager. """ return self.get().commit() def abort(self): """ See ITransactionManager. """ return self.get().abort() def __exit__(self, t, v, tb): if v is None: self.commit() else: self.abort() def savepoint(self, optimistic=False): """ See ITransactionManager. """ return self.get().savepoint(optimistic) def attempts(self, number=3): if number <= 0: raise ValueError("number must be positive") while number: number -= 1 if number: yield Attempt(self) else: yield self def _retryable(self, error_type, error): if issubclass(error_type, TransientError): return True for dm in self.get()._resources: should_retry = getattr(dm, 'should_retry', None) if (should_retry is not None) and should_retry(error): return True class ThreadTransactionManager(TransactionManager, threading.local): """Thread-aware transaction manager. Each thread is associated with a unique transaction. """ class Attempt(object): def __init__(self, manager): self.manager = manager def _retry_or_raise(self, t, v, tb): retry = self.manager._retryable(t, v) self.manager.abort() if retry: return retry # suppress the exception if necessary reraise(t, v, tb) # otherwise reraise the exception def __enter__(self): return self.manager.__enter__() def __exit__(self, t, v, tb): if v is None: try: self.manager.commit() except: return self._retry_or_raise(*sys.exc_info()) else: return self._retry_or_raise(t, v, tb) transaction-1.4.3/transaction/interfaces.py0000664000175000017500000004543512073316126021010 0ustar tseavertseaver############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import zope.interface class ITransactionManager(zope.interface.Interface): """An object that manages a sequence of transactions. Applications use transaction managers to establish transaction boundaries. """ def begin(): """Begin a new transaction. If an existing transaction is in progress, it will be aborted. The newTransaction() method of registered synchronizers is called, passing the new transaction object. """ def get(): """Get the current transaction. """ def commit(): """Commit the current transaction. """ def abort(): """Abort the current transaction. """ def doom(): """Doom the current transaction. """ def isDoomed(): """Returns True if the current transaction is doomed, otherwise False. """ def savepoint(optimistic=False): """Create a savepoint from the current transaction. If the optimistic argument is true, then data managers that don't support savepoints can be used, but an error will be raised if the savepoint is rolled back. An ISavepoint object is returned. """ def registerSynch(synch): """Register an ISynchronizer. Synchronizers are notified about some major events in a transaction's life. See ISynchronizer for details. """ def unregisterSynch(synch): """Unregister an ISynchronizer. Synchronizers are notified about some major events in a transaction's life. See ISynchronizer for details. """ class ITransaction(zope.interface.Interface): """Object representing a running transaction. Objects with this interface may represent different transactions during their lifetime (.begin() can be called to start a new transaction using the same instance, although that example is deprecated and will go away in ZODB 3.6). """ user = zope.interface.Attribute( """A user name associated with the transaction. The format of the user name is defined by the application. The value is of Python type str. Storages record the user value, as meta-data, when a transaction commits. A storage may impose a limit on the size of the value; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or truncate the value). """) description = zope.interface.Attribute( """A textual description of the transaction. The value is of Python type str. Method note() is the intended way to set the value. Storages record the description, as meta-data, when a transaction commits. A storage may impose a limit on the size of the description; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or truncate the value). """) def commit(): """Finalize the transaction. This executes the two-phase commit algorithm for all IDataManager objects associated with the transaction. """ def abort(): """Abort the transaction. This is called from the application. This can only be called before the two-phase commit protocol has been started. """ def doom(): """Doom the transaction. Dooms the current transaction. This will cause DoomedTransactionException to be raised on any attempt to commit the transaction. Otherwise the transaction will behave as if it was active. """ def savepoint(optimistic=False): """Create a savepoint. If the optimistic argument is true, then data managers that don't support savepoints can be used, but an error will be raised if the savepoint is rolled back. An ISavepoint object is returned. """ def join(datamanager): """Add a data manager to the transaction. `datamanager` must provide the transactions.interfaces.IDataManager interface. """ def note(text): """Add text to the transaction description. This modifies the `.description` attribute; see its docs for more detail. First surrounding whitespace is stripped from `text`. If `.description` is currently an empty string, then the stripped text becomes its value, else two newlines and the stripped text are appended to `.description`. """ def setUser(user_name, path="/"): """Set the user name. path should be provided if needed to further qualify the identified user. This is a convenience method used by Zope. It sets the .user attribute to str(path) + " " + str(user_name). This sets the `.user` attribute; see its docs for more detail. """ def setExtendedInfo(name, value): """Add extension data to the transaction. name is the name of the extension property to set, of Python type str; value must be picklable. Multiple calls may be made to set multiple extension properties, provided the names are distinct. Storages record the extension data, as meta-data, when a transaction commits. A storage may impose a limit on the size of extension data; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or remove `` pairs). """ # deprecated38 def beforeCommitHook(__hook, *args, **kws): """Register a hook to call before the transaction is committed. THIS IS DEPRECATED IN ZODB 3.6. Use addBeforeCommitHook() instead. The specified hook function will be called after the transaction's commit method has been called, but before the commit process has been started. The hook will be passed the specified positional and keyword arguments. Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks. Hooks are called only for a top-level commit. A savepoint does not call any hooks. If the transaction is aborted, hooks are not called, and are discarded. Calling a hook "consumes" its registration too: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then beforeCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead. """ def addBeforeCommitHook(hook, args=(), kws=None): """Register a hook to call before the transaction is committed. The specified hook function will be called after the transaction's commit method has been called, but before the commit process has been started. The hook will be passed the specified positional (`args`) and keyword (`kws`) arguments. `args` is a sequence of positional arguments to be passed, defaulting to an empty tuple (no positional arguments are passed). `kws` is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed). Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks. Hooks are called only for a top-level commit. A savepoint creation does not call any hooks. If the transaction is aborted, hooks are not called, and are discarded. Calling a hook "consumes" its registration too: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then addBeforeCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead. """ def getBeforeCommitHooks(): """Return iterable producing the registered addBeforeCommit hooks. A triple (hook, args, kws) is produced for each registered hook. The hooks are produced in the order in which they would be invoked by a top-level transaction commit. """ def addAfterCommitHook(hook, args=(), kws=None): """Register a hook to call after a transaction commit attempt. The specified hook function will be called after the transaction commit succeeds or aborts. The first argument passed to the hook is a Boolean value, true if the commit succeeded, or false if the commit aborted. `args` specifies additional positional, and `kws` keyword, arguments to pass to the hook. `args` is a sequence of positional arguments to be passed, defaulting to an empty tuple (only the true/false success argument is passed). `kws` is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed). Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks. Hooks are called only for a top-level commit. A savepoint creation does not call any hooks. Calling a hook "consumes" its registration: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then addAfterCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead. """ def getAfterCommitHooks(): """Return iterable producing the registered addAfterCommit hooks. A triple (hook, args, kws) is produced for each registered hook. The hooks are produced in the order in which they would be invoked by a top-level transaction commit. """ class ITransactionDeprecated(zope.interface.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(zope.interface.Interface): """Objects that manage transactional storage. These objects may manage data for other objects, or they may manage non-object storages, such as relational databases. For example, a ZODB.Connection. Note that when some data is modified, that data's data manager should join a transaction so that data can be committed when the user commits the transaction. """ transaction_manager = zope.interface.Attribute( """The transaction manager (TM) used by this data manager. This is a public attribute, intended for read-only use. The value is an instance of ITransactionManager, typically set by the data manager's constructor. """) def abort(transaction): """Abort a transaction and forget all changes. Abort must be called outside of a two-phase commit. Abort is called by the transaction manager to abort transactions that are not yet in a two-phase commit. It may also be called when rolling back a savepoint made before the data manager joined the transaction. In any case, after abort is called, the data manager is no longer participating in the transaction. If there are new changes, the data manager must rejoin the transaction. """ # Two-phase commit protocol. These methods are called by the ITransaction # object associated with the transaction being committed. The sequence # of calls normally follows this regular expression: # tpc_begin commit tpc_vote (tpc_finish | tpc_abort) def tpc_begin(transaction): """Begin commit of a transaction, starting the two-phase commit. transaction is the ITransaction instance associated with the transaction being committed. """ def commit(transaction): """Commit modifications to registered objects. Save changes to be made persistent if the transaction commits (if tpc_finish is called later). If tpc_abort is called later, changes must not persist. This includes conflict detection and handling. If no conflicts or errors occur, the data manager should be prepared to make the changes persist when tpc_finish is called. """ def tpc_vote(transaction): """Verify that a data manager can commit the transaction. This is the last chance for a data manager to vote 'no'. A data manager votes 'no' by raising an exception. transaction is the ITransaction instance associated with the transaction being committed. """ def tpc_finish(transaction): """Indicate confirmation that the transaction is done. Make all changes to objects modified by this transaction persist. transaction is the ITransaction instance associated with the transaction being committed. This should never fail. If this raises an exception, the database is not expected to maintain consistency; it's a serious error. """ def tpc_abort(transaction): """Abort a transaction. This is called by a transaction manager to end a two-phase commit on the data manager. Abandon all changes to objects modified by this transaction. transaction is the ITransaction instance associated with the transaction being committed. This should never fail. """ def sortKey(): """Return a key to use for ordering registered DataManagers. In order to guarantee a total ordering, keys must be strings. ZODB uses a global sort order to prevent deadlock when it commits transactions involving multiple resource managers. The resource manager must define a sortKey() method that provides a global ordering for resource managers. """ # Alternate version: #"""Return a consistent sort key for this connection. # #This allows ordering multiple connections that use the same storage in #a consistent manner. This is unique for the lifetime of a connection, #which is good enough to avoid ZEO deadlocks. #""" class ISavepointDataManager(IDataManager): def savepoint(): """Return a data-manager savepoint (IDataManagerSavepoint). """ class IDataManagerSavepoint(zope.interface.Interface): """Savepoint for data-manager changes for use in transaction savepoints. Datamanager savepoints are used by, and only by, transaction savepoints. Note that data manager savepoints don't have any notion of, or responsibility for, validity. It isn't the responsibility of data-manager savepoints to prevent multiple rollbacks or rollbacks after transaction termination. Preventing invalid savepoint rollback is the responsibility of transaction rollbacks. Application code should never use data-manager savepoints. """ def rollback(): """Rollback any work done since the savepoint. """ class ISavepoint(zope.interface.Interface): """A transaction savepoint. """ def rollback(): """Rollback any work done since the savepoint. InvalidSavepointRollbackError is raised if the savepoint isn't valid. """ valid = zope.interface.Attribute( "Boolean indicating whether the savepoint is valid") class InvalidSavepointRollbackError(Exception): """Attempt to rollback an invalid savepoint. A savepoint may be invalid because: - The surrounding transaction has committed or aborted. - An earlier savepoint in the same transaction has been rolled back. """ class ISynchronizer(zope.interface.Interface): """Objects that participate in the transaction-boundary notification API. """ def beforeCompletion(transaction): """Hook that is called by the transaction at the start of a commit. """ def afterCompletion(transaction): """Hook that is called by the transaction after completing a commit. """ def newTransaction(transaction): """Hook that is called at the start of a transaction. This hook is called when, and only when, a transaction manager's begin() method is called explictly. """ class TransactionError(Exception): """An error occurred due to normal transaction processing.""" class TransactionFailedError(TransactionError): """Cannot perform an operation on a transaction that previously failed. An attempt was made to commit a transaction, or to join a transaction, but this transaction previously raised an exception during an attempt to commit it. The transaction must be explicitly aborted, either by invoking abort() on the transaction, or begin() on its transaction manager. """ class DoomedTransaction(TransactionError): """A commit was attempted on a transaction that was doomed.""" class TransientError(TransactionError): """An error has occured when performing a transaction. It's possible that retrying the transaction will succeed. """ transaction-1.4.3/transaction/tests/0000775000175000017500000000000012312641352017440 5ustar tseavertseavertransaction-1.4.3/transaction/tests/__init__.py0000664000175000017500000000000212073316126021543 0ustar tseavertseaver# transaction-1.4.3/transaction/tests/common.py0000664000175000017500000000414712073316126021312 0ustar tseavertseaver############################################################################## # # Copyright (c) 2012 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## class DummyFile(object): def __init__(self): self._lines = [] def write(self, text): self._lines.append(text) def writelines(self, lines): self._lines.extend(lines) class DummyLogger(object): def __init__(self): self._clear() def _clear(self): self._log = [] def log(self, level, msg, *args, **kw): if args: self._log.append((level, msg % args)) elif kw: self._log.append((level, msg % kw)) else: self._log.append((level, msg)) def debug(self, msg, *args, **kw): self.log('debug', msg, *args, **kw) def error(self, msg, *args, **kw): self.log('error', msg, *args, **kw) def critical(self, msg, *args, **kw): self.log('critical', msg, *args, **kw) class Monkey(object): # context-manager for replacing module names in the scope of a test. def __init__(self, module, **kw): self.module = module self.to_restore = dict([(key, getattr(module, key)) for key in kw]) for key, value in kw.items(): setattr(module, key, value) def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): for key, value in self.to_restore.items(): setattr(self.module, key, value) def assertRaisesEx(e_type, checked, *args, **kw): try: checked(*args, **kw) except e_type as e: return e raise AssertionError("Didn't raise: %s" % e_type.__name__) transaction-1.4.3/transaction/tests/test__manager.py0000664000175000017500000004517712073316126022642 0ustar tseavertseaver############################################################################## # # Copyright (c) 2012 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## import unittest class TransactionManagerTests(unittest.TestCase): def _getTargetClass(self): from transaction import TransactionManager return TransactionManager def _makeOne(self): return self._getTargetClass()() def _makePopulated(self): mgr = self._makeOne() sub1 = DataObject(mgr) sub2 = DataObject(mgr) sub3 = DataObject(mgr) nosub1 = DataObject(mgr, nost=1) return mgr, sub1, sub2, sub3, nosub1 def test_ctor(self): tm = self._makeOne() self.assertTrue(tm._txn is None) self.assertEqual(len(tm._synchs), 0) def test_begin_wo_existing_txn_wo_synchs(self): from transaction._transaction import Transaction tm = self._makeOne() tm.begin() self.assertTrue(isinstance(tm._txn, Transaction)) def test_begin_wo_existing_txn_w_synchs(self): from transaction._transaction import Transaction tm = self._makeOne() synch = DummySynch() tm.registerSynch(synch) tm.begin() self.assertTrue(isinstance(tm._txn, Transaction)) self.assertTrue(tm._txn in synch._txns) def test_begin_w_existing_txn(self): class Existing(object): _aborted = False def abort(self): self._aborted = True tm = self._makeOne() tm._txn = txn = Existing() tm.begin() self.assertFalse(tm._txn is txn) self.assertTrue(txn._aborted) def test_get_wo_existing_txn(self): from transaction._transaction import Transaction tm = self._makeOne() txn = tm.get() self.assertTrue(isinstance(txn, Transaction)) def test_get_w_existing_txn(self): class Existing(object): _aborted = False def abort(self): self._aborted = True tm = self._makeOne() tm._txn = txn = Existing() self.assertTrue(tm.get() is txn) def test_free_w_other_txn(self): from transaction._transaction import Transaction tm = self._makeOne() txn = Transaction() tm.begin() self.assertRaises(ValueError, tm.free, txn) def test_free_w_existing_txn(self): class Existing(object): _aborted = False def abort(self): self._aborted = True tm = self._makeOne() tm._txn = txn = Existing() tm.free(txn) self.assertTrue(tm._txn is None) def test_registerSynch(self): tm = self._makeOne() synch = DummySynch() tm.registerSynch(synch) self.assertEqual(len(tm._synchs), 1) self.assertTrue(synch in tm._synchs) def test_unregisterSynch(self): tm = self._makeOne() synch1 = DummySynch() synch2 = DummySynch() tm.registerSynch(synch1) tm.registerSynch(synch2) tm.unregisterSynch(synch1) self.assertEqual(len(tm._synchs), 1) self.assertFalse(synch1 in tm._synchs) self.assertTrue(synch2 in tm._synchs) def test_isDoomed_wo_existing_txn(self): tm = self._makeOne() self.assertFalse(tm.isDoomed()) tm._txn.doom() self.assertTrue(tm.isDoomed()) def test_isDoomed_w_existing_txn(self): class Existing(object): _doomed = False def isDoomed(self): return self._doomed tm = self._makeOne() tm._txn = txn = Existing() self.assertFalse(tm.isDoomed()) txn._doomed = True self.assertTrue(tm.isDoomed()) def test_doom(self): tm = self._makeOne() txn = tm.get() self.assertFalse(txn.isDoomed()) tm.doom() self.assertTrue(txn.isDoomed()) self.assertTrue(tm.isDoomed()) def test_commit_w_existing_txn(self): class Existing(object): _committed = False def commit(self): self._committed = True tm = self._makeOne() tm._txn = txn = Existing() tm.commit() self.assertTrue(txn._committed) def test_abort_w_existing_txn(self): class Existing(object): _aborted = False def abort(self): self._aborted = True tm = self._makeOne() tm._txn = txn = Existing() tm.abort() self.assertTrue(txn._aborted) def test_as_context_manager_wo_error(self): class _Test(object): _committed = False _aborted = False def commit(self): self._committed = True def abort(self): self._aborted = True tm = self._makeOne() with tm: tm._txn = txn = _Test() self.assertTrue(txn._committed) self.assertFalse(txn._aborted) def test_as_context_manager_w_error(self): class _Test(object): _committed = False _aborted = False def commit(self): self._committed = True def abort(self): self._aborted = True tm = self._makeOne() try: with tm: tm._txn = txn = _Test() 1/0 except ZeroDivisionError: pass self.assertFalse(txn._committed) self.assertTrue(txn._aborted) def test_savepoint_default(self): class _Test(object): _sp = None def savepoint(self, optimistic): self._sp = optimistic tm = self._makeOne() tm._txn = txn = _Test() tm.savepoint() self.assertFalse(txn._sp) def test_savepoint_explicit(self): class _Test(object): _sp = None def savepoint(self, optimistic): self._sp = optimistic tm = self._makeOne() tm._txn = txn = _Test() tm.savepoint(True) self.assertTrue(txn._sp) def test_attempts_w_invalid_count(self): tm = self._makeOne() self.assertRaises(ValueError, list, tm.attempts(0)) self.assertRaises(ValueError, list, tm.attempts(-1)) self.assertRaises(ValueError, list, tm.attempts(-10)) def test_attempts_w_valid_count(self): tm = self._makeOne() found = list(tm.attempts(1)) self.assertEqual(len(found), 1) self.assertTrue(found[0] is tm) def test_attempts_w_default_count(self): from transaction._manager import Attempt tm = self._makeOne() found = list(tm.attempts()) self.assertEqual(len(found), 3) for attempt in found[:-1]: self.assertTrue(isinstance(attempt, Attempt)) self.assertTrue(attempt.manager is tm) self.assertTrue(found[-1] is tm) def test__retryable_w_transient_error(self): from transaction.interfaces import TransientError tm = self._makeOne() self.assertTrue(tm._retryable(TransientError, object())) def test__retryable_w_transient_subclass(self): from transaction.interfaces import TransientError class _Derived(TransientError): pass tm = self._makeOne() self.assertTrue(tm._retryable(_Derived, object())) def test__retryable_w_normal_exception_no_resources(self): tm = self._makeOne() self.assertFalse(tm._retryable(Exception, object())) def test__retryable_w_normal_exception_w_resource_voting_yes(self): class _Resource(object): def should_retry(self, err): return True tm = self._makeOne() tm.get()._resources.append(_Resource()) self.assertTrue(tm._retryable(Exception, object())) # basic tests with two sub trans jars # really we only need one, so tests for # sub1 should identical to tests for sub2 def test_commit_normal(self): mgr, sub1, sub2, sub3, nosub1 = self._makePopulated() sub1.modify() sub2.modify() mgr.commit() assert sub1._p_jar.ccommit_sub == 0 assert sub1._p_jar.ctpc_finish == 1 def test_abort_normal(self): mgr, sub1, sub2, sub3, nosub1 = self._makePopulated() sub1.modify() sub2.modify() mgr.abort() assert sub2._p_jar.cabort == 1 # repeat adding in a nonsub trans jars def test_commit_w_nonsub_jar(self): mgr, sub1, sub2, sub3, nosub1 = self._makePopulated() nosub1.modify() mgr.commit() assert nosub1._p_jar.ctpc_finish == 1 def test_abort_w_nonsub_jar(self): mgr, sub1, sub2, sub3, nosub1 = self._makePopulated() nosub1.modify() mgr.abort() assert nosub1._p_jar.ctpc_finish == 0 assert nosub1._p_jar.cabort == 1 ### Failure Mode Tests # # ok now we do some more interesting # tests that check the implementations # error handling by throwing errors from # various jar methods ### # first the recoverable errors def test_abort_w_broken_jar(self): from transaction import _transaction from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): mgr, sub1, sub2, sub3, nosub1 = self._makePopulated() sub1._p_jar = BasicJar(errors='abort') nosub1.modify() sub1.modify(nojar=1) sub2.modify() try: mgr.abort() except TestTxnException: pass assert nosub1._p_jar.cabort == 1 assert sub2._p_jar.cabort == 1 def test_commit_w_broken_jar_commit(self): from transaction import _transaction from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): mgr, sub1, sub2, sub3, nosub1 = self._makePopulated() sub1._p_jar = BasicJar(errors='commit') nosub1.modify() sub1.modify(nojar=1) try: mgr.commit() except TestTxnException: pass assert nosub1._p_jar.ctpc_finish == 0 assert nosub1._p_jar.ccommit == 1 assert nosub1._p_jar.ctpc_abort == 1 def test_commit_w_broken_jar_tpc_vote(self): from transaction import _transaction from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): mgr, sub1, sub2, sub3, nosub1 = self._makePopulated() sub1._p_jar = BasicJar(errors='tpc_vote') nosub1.modify() sub1.modify(nojar=1) try: mgr.commit() except TestTxnException: pass assert nosub1._p_jar.ctpc_finish == 0 assert nosub1._p_jar.ccommit == 1 assert nosub1._p_jar.ctpc_abort == 1 assert sub1._p_jar.ctpc_abort == 1 def test_commit_w_broken_jar_tpc_begin(self): # ok this test reveals a bug in the TM.py # as the nosub tpc_abort there is ignored. # nosub calling method tpc_begin # nosub calling method commit # sub calling method tpc_begin # sub calling method abort # sub calling method tpc_abort # nosub calling method tpc_abort from transaction import _transaction from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): mgr, sub1, sub2, sub3, nosub1 = self._makePopulated() sub1._p_jar = BasicJar(errors='tpc_begin') nosub1.modify() sub1.modify(nojar=1) try: mgr.commit() except TestTxnException: pass assert nosub1._p_jar.ctpc_abort == 1 assert sub1._p_jar.ctpc_abort == 1 def test_commit_w_broken_jar_tpc_abort_tpc_vote(self): from transaction import _transaction from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): mgr, sub1, sub2, sub3, nosub1 = self._makePopulated() sub1._p_jar = BasicJar(errors=('tpc_abort', 'tpc_vote')) nosub1.modify() sub1.modify(nojar=1) try: mgr.commit() except TestTxnException: pass assert nosub1._p_jar.ctpc_abort == 1 class AttemptTests(unittest.TestCase): def _makeOne(self, manager): from transaction._manager import Attempt return Attempt(manager) def test___enter__(self): manager = DummyManager() inst = self._makeOne(manager) inst.__enter__() self.assertTrue(manager.entered) def test___exit__no_exc_no_commit_exception(self): manager = DummyManager() inst = self._makeOne(manager) result = inst.__exit__(None, None, None) self.assertFalse(result) self.assertTrue(manager.committed) def test___exit__no_exc_nonretryable_commit_exception(self): manager = DummyManager(raise_on_commit=ValueError) inst = self._makeOne(manager) self.assertRaises(ValueError, inst.__exit__, None, None, None) self.assertTrue(manager.committed) self.assertTrue(manager.aborted) def test___exit__no_exc_abort_exception_after_nonretryable_commit_exc(self): manager = DummyManager(raise_on_abort=ValueError, raise_on_commit=KeyError) inst = self._makeOne(manager) self.assertRaises(ValueError, inst.__exit__, None, None, None) self.assertTrue(manager.committed) self.assertTrue(manager.aborted) def test___exit__no_exc_retryable_commit_exception(self): from transaction.interfaces import TransientError manager = DummyManager(raise_on_commit=TransientError) inst = self._makeOne(manager) result = inst.__exit__(None, None, None) self.assertTrue(result) self.assertTrue(manager.committed) self.assertTrue(manager.aborted) def test___exit__with_exception_value_retryable(self): from transaction.interfaces import TransientError manager = DummyManager() inst = self._makeOne(manager) result = inst.__exit__(TransientError, TransientError(), None) self.assertTrue(result) self.assertFalse(manager.committed) self.assertTrue(manager.aborted) def test___exit__with_exception_value_nonretryable(self): manager = DummyManager() inst = self._makeOne(manager) self.assertRaises(KeyError, inst.__exit__, KeyError, KeyError(), None) self.assertFalse(manager.committed) self.assertTrue(manager.aborted) class DummyManager(object): entered = False committed = False aborted = False def __init__(self, raise_on_commit=None, raise_on_abort=None): self.raise_on_commit = raise_on_commit self.raise_on_abort = raise_on_abort def _retryable(self, t, v): from transaction._manager import TransientError return issubclass(t, TransientError) def __enter__(self): self.entered = True def abort(self): self.aborted = True if self.raise_on_abort: raise self.raise_on_abort def commit(self): self.committed = True if self.raise_on_commit: raise self.raise_on_commit class DataObject: def __init__(self, transaction_manager, nost=0): self.transaction_manager = transaction_manager self.nost = nost self._p_jar = None def modify(self, nojar=0, tracing=0): if not nojar: if self.nost: self._p_jar = BasicJar(tracing=tracing) else: self._p_jar = BasicJar(tracing=tracing) self.transaction_manager.get().join(self._p_jar) class TestTxnException(Exception): pass class BasicJar: def __init__(self, errors=(), tracing=0): if not isinstance(errors, tuple): errors = errors, self.errors = errors self.tracing = tracing self.cabort = 0 self.ccommit = 0 self.ctpc_begin = 0 self.ctpc_abort = 0 self.ctpc_vote = 0 self.ctpc_finish = 0 self.cabort_sub = 0 self.ccommit_sub = 0 def __repr__(self): return "<%s %X %s>" % (self.__class__.__name__, positive_id(self), self.errors) def sortKey(self): # All these jars use the same sort key, and Python's list.sort() # is stable. These two return self.__class__.__name__ def check(self, method): if self.tracing: print('%s calling method %s'%(str(self.tracing),method)) if method in self.errors: raise TestTxnException("error %s" % method) ## basic jar txn interface def abort(self, *args): self.check('abort') self.cabort += 1 def commit(self, *args): self.check('commit') self.ccommit += 1 def tpc_begin(self, txn, sub=0): self.check('tpc_begin') self.ctpc_begin += 1 def tpc_vote(self, *args): self.check('tpc_vote') self.ctpc_vote += 1 def tpc_abort(self, *args): self.check('tpc_abort') self.ctpc_abort += 1 def tpc_finish(self, *args): self.check('tpc_finish') self.ctpc_finish += 1 class DummySynch(object): def __init__(self): self._txns = set() def newTransaction(self, txn): self._txns.add(txn) def positive_id(obj): """Return id(obj) as a non-negative integer.""" import struct _ADDRESS_MASK = 256 ** struct.calcsize('P') result = id(obj) if result < 0: result += _ADDRESS_MASK assert result > 0 return result def test_suite(): return unittest.TestSuite(( unittest.makeSuite(TransactionManagerTests), unittest.makeSuite(AttemptTests), )) transaction-1.4.3/transaction/tests/test_weakset.py0000664000175000017500000000501512312641051022511 0ustar tseavertseaver############################################################################## # # Copyright (c) 2007 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## import unittest class WeakSetTests(unittest.TestCase): def test_contains(self): from transaction.weakset import WeakSet w = WeakSet() dummy = Dummy() w.add(dummy) self.assertEqual(dummy in w, True) dummy2 = Dummy() self.assertEqual(dummy2 in w, False) def test_len(self): import gc from transaction.weakset import WeakSet w = WeakSet() d1 = Dummy() d2 = Dummy() w.add(d1) w.add(d2) self.assertEqual(len(w), 2) del d1 gc.collect() self.assertEqual(len(w), 1) def test_remove(self): from transaction.weakset import WeakSet w = WeakSet() dummy = Dummy() w.add(dummy) self.assertEqual(dummy in w, True) w.remove(dummy) self.assertEqual(dummy in w, False) def test_as_weakref_list(self): import gc from transaction.weakset import WeakSet w = WeakSet() dummy = Dummy() dummy2 = Dummy() dummy3 = Dummy() w.add(dummy) w.add(dummy2) w.add(dummy3) del dummy3 gc.collect() refs = w.as_weakref_list() self.assertTrue(isinstance(refs, list)) L = [x() for x in refs] # L is a list, but it does not have a guaranteed order. self.assertTrue(list, type(L)) self.assertEqual(set(L), set([dummy, dummy2])) def test_map(self): from transaction.weakset import WeakSet w = WeakSet() dummy = Dummy() dummy2 = Dummy() dummy3 = Dummy() w.add(dummy) w.add(dummy2) w.add(dummy3) def poker(x): x.poked = 1 w.map(poker) for thing in dummy, dummy2, dummy3: self.assertEqual(thing.poked, 1) class Dummy: pass def test_suite(): return unittest.makeSuite(WeakSetTests) transaction-1.4.3/transaction/tests/test_savepoint.py0000664000175000017500000000457512073316126023076 0ustar tseavertseaver############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import unittest class SavepointTests(unittest.TestCase): def testRollbackRollsbackDataManagersThatJoinedLater(self): # A savepoint needs to not just rollback it's savepoints, but needs # to # rollback savepoints for data managers that joined savepoints # after the savepoint: import transaction from transaction.tests import savepointsample dm = savepointsample.SampleSavepointDataManager() dm['name'] = 'bob' sp1 = transaction.savepoint() dm['job'] = 'geek' sp2 = transaction.savepoint() dm['salary'] = 'fun' dm2 = savepointsample.SampleSavepointDataManager() dm2['name'] = 'sally' self.assertTrue('name' in dm) self.assertTrue('job' in dm) self.assertTrue('salary' in dm) self.assertTrue('name' in dm2) sp1.rollback() self.assertTrue('name' in dm) self.assertFalse('job' in dm) self.assertFalse('salary' in dm) self.assertFalse('name' in dm2) def test_commit_after_rollback_for_dm_that_joins_after_savepoint(self): # There was a problem handling data managers that joined after a # savepoint. If the savepoint was rolled back and then changes # made, the dm would end up being joined twice, leading to extra # tpc calls and pain. import transaction from transaction.tests import savepointsample sp = transaction.savepoint() dm = savepointsample.SampleSavepointDataManager() dm['name'] = 'bob' sp.rollback() dm['name'] = 'Bob' transaction.commit() self.assertEqual(dm['name'], 'Bob') def test_suite(): return unittest.TestSuite(( unittest.makeSuite(SavepointTests), )) transaction-1.4.3/transaction/tests/test_register_compat.py0000664000175000017500000001050512073316126024243 0ustar tseavertseaver############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test backwards compatibility for resource managers using register(). The transaction package supports several different APIs for resource managers. The original ZODB3 API was implemented by ZODB.Connection. The Connection passed persistent objects to a Transaction's register() method. It's possible that third-party code also used this API, hence these tests that the code that adapts the old interface to the current API works. These tests use a TestConnection object that implements the old API. They check that the right methods are called and in roughly the right order. """ import unittest class BBBTests(unittest.TestCase): def setUp(self): from transaction import abort abort() tearDown = setUp def test_basic_commit(self): import transaction cn = TestConnection() cn.register(Object()) cn.register(Object()) cn.register(Object()) transaction.commit() self.assertEqual(len(cn.committed), 3) self.assertEqual(len(cn.aborted), 0) self.assertEqual(cn.calls, ['begin', 'vote', 'finish']) def test_basic_abort(self): # If the application calls abort(), then the transaction never gets # into the two-phase commit. It just aborts each object. import transaction cn = TestConnection() cn.register(Object()) cn.register(Object()) cn.register(Object()) transaction.abort() self.assertEqual(len(cn.committed), 0) self.assertEqual(len(cn.aborted), 3) self.assertEqual(cn.calls, []) def test_tpc_error(self): # The tricky part of the implementation is recovering from an error # that occurs during the two-phase commit. We override the commit() # and abort() methods of Object to cause errors during commit. # Note that the implementation uses lists internally, so that objects # are committed in the order they are registered. (In the presence # of multiple resource managers, objects from a single resource # manager are committed in order. I'm not sure if this is an # accident of the implementation or a feature that should be # supported by any implementation.) # The order of resource managers depends on sortKey(). import transaction cn = TestConnection() cn.register(Object()) cn.register(CommitError()) cn.register(Object()) self.assertRaises(RuntimeError, transaction.commit) self.assertEqual(len(cn.committed), 1) self.assertEqual(len(cn.aborted), 3) class Object(object): def commit(self): pass def abort(self): pass class CommitError(Object): def commit(self): raise RuntimeError("commit") class AbortError(Object): def abort(self): raise RuntimeError("abort") class BothError(CommitError, AbortError): pass class TestConnection(object): def __init__(self): self.committed = [] self.aborted = [] self.calls = [] def register(self, obj): import transaction obj._p_jar = self transaction.get().register(obj) def sortKey(self): return str(id(self)) def tpc_begin(self, txn): self.calls.append("begin") def tpc_vote(self, txn): self.calls.append("vote") def tpc_finish(self, txn): self.calls.append("finish") def tpc_abort(self, txn): self.calls.append("abort") def commit(self, obj, txn): obj.commit() self.committed.append(obj) def abort(self, obj, txn): obj.abort() self.aborted.append(obj) def test_suite(): return unittest.TestSuite(( unittest.makeSuite(BBBTests), )) transaction-1.4.3/transaction/tests/warnhook.py0000664000175000017500000000410212073316126021641 0ustar tseavertseaver############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import warnings class WarningsHook: """Hook to capture warnings generated by Python. The function warnings.showwarning() is designed to be hooked by application code, allowing the application to customize the way it handles warnings. This hook captures the unformatted warning information and stores it in a list. A test can inspect this list after the test is over. Issues: The warnings module has lots of delicate internal state. If a warning has been reported once, it won't be reported again. It may be necessary to extend this class with a mechanism for modifying the internal state so that we can be guaranteed a warning will be reported. If Python is run with a warnings filter, e.g. python -Werror, then a test that is trying to inspect a particular warning will fail. Perhaps this class can be extended to install more-specific filters the test to work anyway. """ def __init__(self): self.original = None self.warnings = [] def install(self): self.original = warnings.showwarning warnings.showwarning = self.showwarning def uninstall(self): assert self.original is not None warnings.showwarning = self.original self.original = None def showwarning(self, message, category, filename, lineno): self.warnings.append((str(message), category, filename, lineno)) def clear(self): self.warnings = [] transaction-1.4.3/transaction/tests/examples.py0000664000175000017500000001255312073316126021640 0ustar tseavertseaver############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample objects for use in tests """ class DataManager(object): """Sample data manager Used by the 'datamanager' chapter in the Sphinx docs. """ def __init__(self): self.state = 0 self.sp = 0 self.transaction = None self.delta = 0 self.prepared = False def inc(self, n=1): self.delta += n def prepare(self, transaction): if self.prepared: raise TypeError('Already prepared') self._checkTransaction(transaction) self.prepared = True self.transaction = transaction self.state += self.delta def _checkTransaction(self, transaction): if (transaction is not self.transaction and self.transaction is not None): raise TypeError("Transaction missmatch", transaction, self.transaction) def abort(self, transaction): self._checkTransaction(transaction) if self.transaction is not None: self.transaction = None if self.prepared: self.state -= self.delta self.prepared = False self.delta = 0 def commit(self, transaction): if not self.prepared: raise TypeError('Not prepared to commit') self._checkTransaction(transaction) self.delta = 0 self.transaction = None self.prepared = False def savepoint(self, transaction): if self.prepared: raise TypeError("Can't get savepoint during two-phase commit") self._checkTransaction(transaction) self.transaction = transaction self.sp += 1 return Rollback(self) class Rollback(object): def __init__(self, dm): self.dm = dm self.sp = dm.sp self.delta = dm.delta self.transaction = dm.transaction def rollback(self): if self.transaction is not self.dm.transaction: raise TypeError("Attempt to rollback stale rollback") if self.dm.sp < self.sp: raise TypeError("Attempt to roll back to invalid save point", self.sp, self.dm.sp) self.dm.sp = self.sp self.dm.delta = self.delta class ResourceManager(object): """ Sample resource manager. Used by the 'resourcemanager' chapter in the Sphinx docs. """ def __init__(self): self.state = 0 self.sp = 0 self.transaction = None self.delta = 0 self.txn_state = None def _check_state(self, *ok_states): if self.txn_state not in ok_states: raise ValueError("txn in state %r but expected one of %r" % (self.txn_state, ok_states)) def _checkTransaction(self, transaction): if (transaction is not self.transaction and self.transaction is not None): raise TypeError("Transaction missmatch", transaction, self.transaction) def inc(self, n=1): self.delta += n def tpc_begin(self, transaction): self._checkTransaction(transaction) self._check_state(None) self.transaction = transaction self.txn_state = 'tpc_begin' def tpc_vote(self, transaction): self._checkTransaction(transaction) self._check_state('tpc_begin') self.state += self.delta self.txn_state = 'tpc_vote' def tpc_finish(self, transaction): self._checkTransaction(transaction) self._check_state('tpc_vote') self.delta = 0 self.transaction = None self.prepared = False self.txn_state = None def tpc_abort(self, transaction): self._checkTransaction(transaction) if self.transaction is not None: self.transaction = None if self.txn_state == 'tpc_vote': self.state -= self.delta self.txn_state = None self.delta = 0 def savepoint(self, transaction): if self.txn_state is not None: raise TypeError("Can't get savepoint during two-phase commit") self._checkTransaction(transaction) self.transaction = transaction self.sp += 1 return SavePoint(self) def discard(self, transaction): pass class SavePoint(object): def __init__(self, rm): self.rm = rm self.sp = rm.sp self.delta = rm.delta self.transaction = rm.transaction def rollback(self): if self.transaction is not self.rm.transaction: raise TypeError("Attempt to rollback stale rollback") if self.rm.sp < self.sp: raise TypeError("Attempt to roll back to invalid save point", self.sp, self.rm.sp) self.rm.sp = self.sp self.rm.delta = self.delta def discard(self): pass transaction-1.4.3/transaction/tests/savepointsample.py0000664000175000017500000001613212073316126023231 0ustar tseavertseaver############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Savepoint data manager implementation example. Sample data manager implementation that illustrates how to implement savepoints. Used by savepoint.rst in the Sphinx docs. """ from zope.interface import implementer import transaction.interfaces @implementer(transaction.interfaces.IDataManager) class SampleDataManager(object): """Sample implementation of data manager that doesn't support savepoints This data manager stores named simple values, like strings and numbers. """ def __init__(self, transaction_manager=None): if transaction_manager is None: # Use the thread-local transaction manager if none is provided: import transaction transaction_manager = transaction.manager self.transaction_manager = transaction_manager # Our committed and uncommitted data: self.committed = {} self.uncommitted = self.committed.copy() # Our transaction state: # # If our uncommitted data is modified, we'll join a transaction # and keep track of the transaction we joined. Any commit # related messages we get should be for this same transaction self.transaction = None # What phase, if any, of two-phase commit we are in: self.tpc_phase = None ####################################################################### # Provide a mapping interface to uncommitted data. We provide # a basic subset of the interface. DictMixin does the rest. def __getitem__(self, name): return self.uncommitted[name] def __setitem__(self, name, value): self._join() # join the current transaction, if we haven't already self.uncommitted[name] = value def __delitem__(self, name): self._join() # join the current transaction, if we haven't already del self.uncommitted[name] def keys(self): return self.uncommitted.keys() __iter__ = keys def __contains__(self, k): return k in self.uncommitted def __len__(self): return len(self.keys()) def __repr__(self): return repr(self.uncommitted) # ####################################################################### ####################################################################### # Transaction methods def _join(self): # If this is the first change in the transaction, join the transaction if self.transaction is None: self.transaction = self.transaction_manager.get() self.transaction.join(self) def _resetTransaction(self): self.last_note = getattr(self.transaction, 'description', None) self.transaction = None self.tpc_phase = None def abort(self, transaction): """Throw away changes made before the commit process has started """ assert ((transaction is self.transaction) or (self.transaction is None) ), "Must not change transactions" assert self.tpc_phase is None, "Must be called outside of tpc" self.uncommitted = self.committed.copy() self._resetTransaction() def tpc_begin(self, transaction): """Enter two-phase commit """ assert transaction is self.transaction, "Must not change transactions" assert self.tpc_phase is None, "Must be called outside of tpc" self.tpc_phase = 1 def commit(self, transaction): """Record data modified during the transaction """ assert transaction is self.transaction, "Must not change transactions" assert self.tpc_phase == 1, "Must be called in first phase of tpc" # In our simple example, we don't need to do anything. # A more complex data manager would typically write to some sort # of log. def tpc_vote(self, transaction): assert transaction is self.transaction, "Must not change transactions" assert self.tpc_phase == 1, "Must be called in first phase of tpc" # This particular data manager is always ready to vote. # Real data managers will usually need to take some steps to # make sure that the finish will succeed self.tpc_phase = 2 def tpc_finish(self, transaction): assert transaction is self.transaction, "Must not change transactions" assert self.tpc_phase == 2, "Must be called in second phase of tpc" self.committed = self.uncommitted.copy() self._resetTransaction() def tpc_abort(self, transaction): assert transaction is self.transaction, "Must not change transactions" assert self.tpc_phase is not None, "Must be called inside of tpc" self.uncommitted = self.committed.copy() self._resetTransaction() # ####################################################################### ####################################################################### # Other data manager methods def sortKey(self): # Commit operations on multiple data managers are performed in # sort key order. This important to avoid deadlock when data # managers are shared among multiple threads or processes and # use locks to manage that sharing. We aren't going to bother # with that here. return str(id(self)) # ####################################################################### @implementer(transaction.interfaces.ISavepointDataManager) class SampleSavepointDataManager(SampleDataManager): """Sample implementation of a savepoint-supporting data manager This extends the basic data manager with savepoint support. """ def savepoint(self): # When we create the savepoint, we save the existing database state. return SampleSavepoint(self, self.uncommitted.copy()) def _rollback_savepoint(self, savepoint): # When we rollback the savepoint, we restore the saved data. # Caution: without the copy(), further changes to the database # could reflect in savepoint.data, and then `savepoint` would no # longer contain the originally saved data, and so `savepoint` # couldn't restore the original state if a rollback to this # savepoint was done again. IOW, copy() is necessary. self.uncommitted = savepoint.data.copy() @implementer(transaction.interfaces.IDataManagerSavepoint) class SampleSavepoint: def __init__(self, data_manager, data): self.data_manager = data_manager self.data = data def rollback(self): self.data_manager._rollback_savepoint(self) transaction-1.4.3/transaction/tests/test__transaction.py0000664000175000017500000015034012073316126023542 0ustar tseavertseaver############################################################################## # # Copyright (c) 2001, 2002, 2005 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## """Test transaction behavior for variety of cases. I wrote these unittests to investigate some odd transaction behavior when doing unittests of integrating non sub transaction aware objects, and to insure proper txn behavior. these tests test the transaction system independent of the rest of the zodb. you can see the method calls to a jar by passing the keyword arg tracing to the modify method of a dataobject. the value of the arg is a prefix used for tracing print calls to that objects jar. the number of times a jar method was called can be inspected by looking at an attribute of the jar that is the method name prefixed with a c (count/check). i've included some tracing examples for tests that i thought were illuminating as doc strings below. TODO add in tests for objects which are modified multiple times, for example an object that gets modified in multiple sub txns. """ import unittest class TransactionTests(unittest.TestCase): def _getTargetClass(self): from transaction._transaction import Transaction return Transaction def _makeOne(self, synchronizers=None, manager=None): return self._getTargetClass()(synchronizers, manager) def test_ctor_defaults(self): from transaction.weakset import WeakSet from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() self.assertTrue(isinstance(txn._synchronizers, WeakSet)) self.assertEqual(len(txn._synchronizers), 0) self.assertTrue(txn._manager is None) self.assertEqual(txn.user, "") self.assertEqual(txn.description, "") self.assertTrue(txn._savepoint2index is None) self.assertEqual(txn._savepoint_index, 0) self.assertEqual(txn._resources, []) self.assertEqual(txn._adapters, {}) self.assertEqual(txn._voted, {}) self.assertEqual(txn._extension, {}) self.assertTrue(txn.log is logger) self.assertEqual(len(logger._log), 1) self.assertEqual(logger._log[0][0], 'debug') self.assertEqual(logger._log[0][1], 'new transaction') self.assertTrue(txn._failure_traceback is None) self.assertEqual(txn._before_commit, []) self.assertEqual(txn._after_commit, []) def test_ctor_w_syncs(self): from transaction.weakset import WeakSet synchs = WeakSet() txn = self._makeOne(synchronizers=synchs) self.assertTrue(txn._synchronizers is synchs) def test_isDoomed(self): from transaction._transaction import Status txn = self._makeOne() self.assertFalse(txn.isDoomed()) txn.status = Status.DOOMED self.assertTrue(txn.isDoomed()) def test_doom_active(self): from transaction._transaction import Status txn = self._makeOne() txn.doom() self.assertTrue(txn.isDoomed()) self.assertEqual(txn.status, Status.DOOMED) def test_doom_invalid(self): from transaction._transaction import Status txn = self._makeOne() for status in Status.COMMITTING, Status.COMMITTED, Status.COMMITFAILED: txn.status = status self.assertRaises(ValueError, txn.doom) def test_doom_already_doomed(self): from transaction._transaction import Status txn = self._makeOne() txn.status = Status.DOOMED self.assertTrue(txn.isDoomed()) self.assertEqual(txn.status, Status.DOOMED) def test__prior_operation_failed(self): from transaction.interfaces import TransactionFailedError from transaction.tests.common import assertRaisesEx class _Traceback(object): def getvalue(self): return 'TRACEBACK' txn = self._makeOne() txn._failure_traceback = _Traceback() err = assertRaisesEx(TransactionFailedError, txn._prior_operation_failed) self.assertTrue(str(err).startswith('An operation previously failed')) self.assertTrue(str(err).endswith( "with traceback:\n\nTRACEBACK")) def test_join_COMMITFAILED(self): from transaction.interfaces import TransactionFailedError from transaction._transaction import Status class _Traceback(object): def getvalue(self): return 'TRACEBACK' txn = self._makeOne() txn.status = Status.COMMITFAILED txn._failure_traceback = _Traceback() self.assertRaises(TransactionFailedError, txn.join, object()) def test_join_COMMITTING(self): from transaction._transaction import Status txn = self._makeOne() txn.status = Status.COMMITTING self.assertRaises(ValueError, txn.join, object()) def test_join_COMMITTED(self): from transaction._transaction import Status txn = self._makeOne() txn.status = Status.COMMITTED self.assertRaises(ValueError, txn.join, object()) def test_join_DOOMED_non_preparing_wo_sp2index(self): from transaction._transaction import Status txn = self._makeOne() txn.status = Status.DOOMED resource = object() txn.join(resource) self.assertEqual(txn._resources, [resource]) def test_join_ACTIVE_w_preparing_w_sp2index(self): from transaction._transaction import AbortSavepoint from transaction._transaction import DataManagerAdapter class _TSP(object): def __init__(self): self._savepoints = [] class _DM(object): def prepare(self): pass txn = self._makeOne() tsp = _TSP() txn._savepoint2index = {tsp: object()} dm = _DM txn.join(dm) self.assertEqual(len(txn._resources), 1) dma = txn._resources[0] self.assertTrue(isinstance(dma, DataManagerAdapter)) self.assertTrue(txn._resources[0]._datamanager is dm) self.assertEqual(len(tsp._savepoints), 1) self.assertTrue(isinstance(tsp._savepoints[0], AbortSavepoint)) self.assertTrue(tsp._savepoints[0].datamanager is dma) self.assertTrue(tsp._savepoints[0].transaction is txn) def test__unjoin_miss(self): txn = self._makeOne() txn._unjoin(object()) #no raise def test__unjoin_hit(self): txn = self._makeOne() resource = object() txn._resources.append(resource) txn._unjoin(resource) self.assertEqual(txn._resources, []) def test_savepoint_COMMITFAILED(self): from transaction.interfaces import TransactionFailedError from transaction._transaction import Status class _Traceback(object): def getvalue(self): return 'TRACEBACK' txn = self._makeOne() txn.status = Status.COMMITFAILED txn._failure_traceback = _Traceback() self.assertRaises(TransactionFailedError, txn.savepoint) def test_savepoint_empty(self): from weakref import WeakKeyDictionary from transaction import _transaction from transaction._transaction import Savepoint from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() sp = txn.savepoint() self.assertTrue(isinstance(sp, Savepoint)) self.assertTrue(sp.transaction is txn) self.assertEqual(sp._savepoints, []) self.assertEqual(txn._savepoint_index, 1) self.assertTrue(isinstance(txn._savepoint2index, WeakKeyDictionary)) self.assertEqual(txn._savepoint2index[sp], 1) def test_savepoint_non_optimistc_resource_wo_support(self): from transaction import _transaction from transaction._transaction import Status from transaction._compat import StringIO from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() logger._clear() resource = object() txn._resources.append(resource) self.assertRaises(TypeError, txn.savepoint) self.assertEqual(txn.status, Status.COMMITFAILED) self.assertTrue(isinstance(txn._failure_traceback, StringIO)) self.assertTrue('TypeError' in txn._failure_traceback.getvalue()) self.assertEqual(len(logger._log), 2) self.assertEqual(logger._log[0][0], 'error') self.assertTrue(logger._log[0][1].startswith('Error in abort')) self.assertEqual(logger._log[1][0], 'error') self.assertTrue(logger._log[1][1].startswith('Error in tpc_abort')) def test__remove_and_invalidate_after_miss(self): from weakref import WeakKeyDictionary txn = self._makeOne() txn._savepoint2index = WeakKeyDictionary() class _SP(object): def __init__(self, txn): self.transaction = txn holdme = [] for i in range(10): sp = _SP(txn) holdme.append(sp) #prevent gc txn._savepoint2index[sp] = i self.assertEqual(len(txn._savepoint2index), 10) self.assertRaises(KeyError, txn._remove_and_invalidate_after, _SP(txn)) self.assertEqual(len(txn._savepoint2index), 10) def test__remove_and_invalidate_after_hit(self): from weakref import WeakKeyDictionary txn = self._makeOne() txn._savepoint2index = WeakKeyDictionary() class _SP(object): def __init__(self, txn, index): self.transaction = txn self._index = index def __lt__(self, other): return self._index < other._index def __repr__(self): return '_SP: %d' % self._index holdme = [] for i in range(10): sp = _SP(txn, i) holdme.append(sp) #prevent gc txn._savepoint2index[sp] = i self.assertEqual(len(txn._savepoint2index), 10) txn._remove_and_invalidate_after(holdme[1]) self.assertEqual(sorted(txn._savepoint2index), sorted(holdme[:2])) def test__invalidate_all_savepoints(self): from weakref import WeakKeyDictionary txn = self._makeOne() txn._savepoint2index = WeakKeyDictionary() class _SP(object): def __init__(self, txn, index): self.transaction = txn self._index = index def __repr__(self): return '_SP: %d' % self._index holdme = [] for i in range(10): sp = _SP(txn, i) holdme.append(sp) #prevent gc txn._savepoint2index[sp] = i self.assertEqual(len(txn._savepoint2index), 10) txn._invalidate_all_savepoints() self.assertEqual(list(txn._savepoint2index), []) def test_register_wo_jar(self): class _Dummy(object): _p_jar = None txn = self._makeOne() self.assertRaises(ValueError, txn.register, _Dummy()) def test_register_w_jar(self): class _Manager(object): pass mgr = _Manager() class _Dummy(object): _p_jar = mgr txn = self._makeOne() dummy = _Dummy() txn.register(dummy) resources = list(txn._resources) self.assertEqual(len(resources), 1) adapter = resources[0] self.assertTrue(adapter.manager is mgr) self.assertTrue(dummy in adapter.objects) items = list(txn._adapters.items()) self.assertEqual(len(items), 1) self.assertTrue(items[0][0] is mgr) self.assertTrue(items[0][1] is adapter) def test_register_w_jar_already_adapted(self): class _Adapter(object): def __init__(self): self.objects = [] class _Manager(object): pass mgr = _Manager() class _Dummy(object): _p_jar = mgr txn = self._makeOne() txn._adapters[mgr] = adapter = _Adapter() dummy = _Dummy() txn.register(dummy) self.assertTrue(dummy in adapter.objects) def test_commit_DOOMED(self): from transaction.interfaces import DoomedTransaction from transaction._transaction import Status txn = self._makeOne() txn.status = Status.DOOMED self.assertRaises(DoomedTransaction, txn.commit) def test_commit_COMMITFAILED(self): from transaction._transaction import Status from transaction.interfaces import TransactionFailedError class _Traceback(object): def getvalue(self): return 'TRACEBACK' txn = self._makeOne() txn.status = Status.COMMITFAILED txn._failure_traceback = _Traceback() self.assertRaises(TransactionFailedError, txn.commit) def test_commit_wo_savepoints_wo_hooks_wo_synchronizers(self): from transaction._transaction import Status from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction class _Mgr(object): def __init__(self, txn): self._txn = txn def free(self, txn): assert txn is self._txn self._txn = None logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() logger._clear() mgr = txn._manager = _Mgr(txn) txn.commit() self.assertEqual(txn.status, Status.COMMITTED) self.assertTrue(mgr._txn is None) self.assertEqual(logger._log[0][0], 'debug') self.assertEqual(logger._log[0][1], 'commit') def test_commit_w_savepoints(self): from weakref import WeakKeyDictionary from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction class _SP(object): def __init__(self, txn, index): self.transaction = txn self._index = index def __repr__(self): return '_SP: %d' % self._index logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() txn._savepoint2index = WeakKeyDictionary() holdme = [] for i in range(10): sp = _SP(txn, i) holdme.append(sp) #prevent gc txn._savepoint2index[sp] = i logger._clear() txn.commit() self.assertEqual(list(txn._savepoint2index), []) def test_commit_w_beforeCommitHooks(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction _hooked1, _hooked2 = [], [] def _hook1(*args, **kw): _hooked1.append((args, kw)) def _hook2(*args, **kw): _hooked2.append((args, kw)) logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() txn._before_commit.append((_hook1, ('one',), {'uno': 1})) txn._before_commit.append((_hook2, (), {})) logger._clear() txn.commit() self.assertEqual(_hooked1, [(('one',), {'uno': 1})]) self.assertEqual(_hooked2, [((), {})]) self.assertEqual(txn._before_commit, []) def test_commit_w_synchronizers(self): from transaction.weakset import WeakSet from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction class _Synch(object): _before = _after = False def beforeCompletion(self, txn): self._before = txn def afterCompletion(self, txn): self._after = txn synchs = [_Synch(), _Synch(), _Synch()] ws = WeakSet() for synch in synchs: ws.add(synch) logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne(synchronizers=ws) logger._clear() txn.commit() for synch in synchs: self.assertTrue(synch._before is txn) self.assertTrue(synch._after is txn) def test_commit_w_afterCommitHooks(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction _hooked1, _hooked2 = [], [] def _hook1(*args, **kw): _hooked1.append((args, kw)) def _hook2(*args, **kw): _hooked2.append((args, kw)) logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() txn._after_commit.append((_hook1, ('one',), {'uno': 1})) txn._after_commit.append((_hook2, (), {})) logger._clear() txn.commit() self.assertEqual(_hooked1, [((True, 'one',), {'uno': 1})]) self.assertEqual(_hooked2, [((True,), {})]) self.assertEqual(txn._after_commit, []) def test_commit_error_w_afterCompleteHooks(self): from transaction import _transaction from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey class BrokenResource(object): def sortKey(self): return 'zzz' def tpc_begin(self, txn): raise ValueError('test') broken = BrokenResource() resource = Resource('aaa') _hooked1, _hooked2 = [], [] def _hook1(*args, **kw): _hooked1.append((args, kw)) def _hook2(*args, **kw): _hooked2.append((args, kw)) logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() txn._after_commit.append((_hook1, ('one',), {'uno': 1})) txn._after_commit.append((_hook2, (), {})) txn._resources.append(broken) txn._resources.append(resource) logger._clear() self.assertRaises(ValueError, txn.commit) self.assertEqual(_hooked1, [((False, 'one',), {'uno': 1})]) self.assertEqual(_hooked2, [((False,), {})]) self.assertEqual(txn._after_commit, []) self.assertTrue(resource._b) self.assertFalse(resource._c) self.assertFalse(resource._v) self.assertFalse(resource._f) self.assertTrue(resource._a) self.assertTrue(resource._x) def test_commit_error_w_synchronizers(self): from transaction.weakset import WeakSet from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction class _Synch(object): _before = _after = False def beforeCompletion(self, txn): self._before = txn def afterCompletion(self, txn): self._after = txn synchs = [_Synch(), _Synch(), _Synch()] ws = WeakSet() for synch in synchs: ws.add(synch) class BrokenResource(object): def sortKey(self): return 'zzz' def tpc_begin(self, txn): raise ValueError('test') broken = BrokenResource() logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne(synchronizers=ws) logger._clear() txn._resources.append(broken) self.assertRaises(ValueError, txn.commit) for synch in synchs: self.assertTrue(synch._before is txn) self.assertTrue(synch._after is txn) #called in _cleanup def test_getBeforeCommitHooks_empty(self): txn = self._makeOne() self.assertEqual(list(txn.getBeforeCommitHooks()), []) def test_addBeforeCommitHook(self): def _hook(*args, **kw): pass txn = self._makeOne() txn.addBeforeCommitHook(_hook, ('one',), dict(uno=1)) self.assertEqual(list(txn.getBeforeCommitHooks()), [(_hook, ('one',), {'uno': 1})]) def test_addBeforeCommitHook_w_kws(self): def _hook(*args, **kw): pass txn = self._makeOne() txn.addBeforeCommitHook(_hook, ('one',)) self.assertEqual(list(txn.getBeforeCommitHooks()), [(_hook, ('one',), {})]) def test_getAfterCommitHooks_empty(self): txn = self._makeOne() self.assertEqual(list(txn.getAfterCommitHooks()), []) def test_addAfterCommitHook(self): def _hook(*args, **kw): pass txn = self._makeOne() txn.addAfterCommitHook(_hook, ('one',), dict(uno=1)) self.assertEqual(list(txn.getAfterCommitHooks()), [(_hook, ('one',), {'uno': 1})]) def test_addAfterCommitHook_wo_kws(self): def _hook(*args, **kw): pass txn = self._makeOne() txn.addAfterCommitHook(_hook, ('one',)) self.assertEqual(list(txn.getAfterCommitHooks()), [(_hook, ('one',), {})]) def test_callAfterCommitHook_w_error(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction _hooked2 = [] def _hook1(*args, **kw): raise ValueError() def _hook2(*args, **kw): _hooked2.append((args, kw)) logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() logger._clear() txn.addAfterCommitHook(_hook1, ('one',)) txn.addAfterCommitHook(_hook2, ('two',), dict(dos=2)) txn._callAfterCommitHooks() # second hook gets called even if first raises self.assertEqual(_hooked2, [((True, 'two',), {'dos': 2})]) self.assertEqual(len(logger._log), 1) self.assertEqual(logger._log[0][0], 'error') self.assertTrue(logger._log[0][1].startswith( "Error in after commit hook")) def test_callAfterCommitHook_w_abort(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction _hooked2 = [] def _hook1(*args, **kw): raise ValueError() def _hook2(*args, **kw): _hooked2.append((args, kw)) logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() logger._clear() txn.addAfterCommitHook(_hook1, ('one',)) txn.addAfterCommitHook(_hook2, ('two',), dict(dos=2)) txn._callAfterCommitHooks() self.assertEqual(logger._log[0][0], 'error') self.assertTrue(logger._log[0][1].startswith( "Error in after commit hook")) def test__commitResources_normal(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction resources = [Resource('bbb'), Resource('aaa')] logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() logger._clear() txn._resources.extend(resources) txn._commitResources() self.assertEqual(len(txn._voted), 2) for r in resources: self.assertTrue(r._b and r._c and r._v and r._f) self.assertFalse(r._a and r._x) self.assertTrue(id(r) in txn._voted) self.assertEqual(len(logger._log), 2) self.assertEqual(logger._log[0][0], 'debug') self.assertEqual(logger._log[0][1], 'commit Resource: aaa') self.assertEqual(logger._log[1][0], 'debug') self.assertEqual(logger._log[1][1], 'commit Resource: bbb') def test__commitResources_error_in_tpc_begin(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction resources = [Resource('bbb', 'tpc_begin'), Resource('aaa')] logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() logger._clear() txn._resources.extend(resources) self.assertRaises(ValueError, txn._commitResources) for r in resources: if r._key == 'aaa': self.assertTrue(r._b) else: self.assertFalse(r._b) self.assertFalse(r._c and r._v and r._f) self.assertTrue(r._a and r._x) self.assertEqual(len(logger._log), 0) def test__commitResources_error_in_commit(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction resources = [Resource('bbb', 'commit'), Resource('aaa')] logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() logger._clear() txn._resources.extend(resources) self.assertRaises(ValueError, txn._commitResources) for r in resources: self.assertTrue(r._b) if r._key == 'aaa': self.assertTrue(r._c) else: self.assertFalse(r._c) self.assertFalse(r._v and r._f) self.assertTrue(r._a and r._x) self.assertEqual(len(logger._log), 1) self.assertEqual(logger._log[0][0], 'debug') self.assertEqual(logger._log[0][1], 'commit Resource: aaa') def test__commitResources_error_in_tpc_vote(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction resources = [Resource('bbb', 'tpc_vote'), Resource('aaa')] logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() logger._clear() txn._resources.extend(resources) self.assertRaises(ValueError, txn._commitResources) self.assertEqual(len(txn._voted), 1) for r in resources: self.assertTrue(r._b and r._c) if r._key == 'aaa': self.assertTrue(id(r) in txn._voted) self.assertTrue(r._v) self.assertFalse(r._f) self.assertFalse(r._a) self.assertTrue(r._x) else: self.assertFalse(id(r) in txn._voted) self.assertFalse(r._v) self.assertFalse(r._f) self.assertTrue(r._a and r._x) self.assertEqual(len(logger._log), 2) self.assertEqual(logger._log[0][0], 'debug') self.assertEqual(logger._log[0][1], 'commit Resource: aaa') self.assertEqual(logger._log[1][0], 'debug') self.assertEqual(logger._log[1][1], 'commit Resource: bbb') def test__commitResources_error_in_tpc_finish(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction resources = [Resource('bbb', 'tpc_finish'), Resource('aaa')] logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() logger._clear() txn._resources.extend(resources) self.assertRaises(ValueError, txn._commitResources) for r in resources: self.assertTrue(r._b and r._c and r._v) self.assertTrue(id(r) in txn._voted) if r._key == 'aaa': self.assertTrue(r._f) else: self.assertFalse(r._f) self.assertFalse(r._a and r._x) #no cleanup if tpc_finish raises self.assertEqual(len(logger._log), 3) self.assertEqual(logger._log[0][0], 'debug') self.assertEqual(logger._log[0][1], 'commit Resource: aaa') self.assertEqual(logger._log[1][0], 'debug') self.assertEqual(logger._log[1][1], 'commit Resource: bbb') self.assertEqual(logger._log[2][0], 'critical') self.assertTrue(logger._log[2][1].startswith( 'A storage error occurred')) def test_abort_wo_savepoints_wo_hooks_wo_synchronizers(self): from transaction._transaction import Status from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction class _Mgr(object): def __init__(self, txn): self._txn = txn def free(self, txn): assert txn is self._txn self._txn = None logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() logger._clear() mgr = txn._manager = _Mgr(txn) txn.abort() self.assertEqual(txn.status, Status.ACTIVE) self.assertTrue(mgr._txn is None) self.assertEqual(logger._log[0][0], 'debug') self.assertEqual(logger._log[0][1], 'abort') def test_abort_w_savepoints(self): from weakref import WeakKeyDictionary from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction class _SP(object): def __init__(self, txn, index): self.transaction = txn self._index = index def __repr__(self): return '_SP: %d' % self._index logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() txn._savepoint2index = WeakKeyDictionary() holdme = [] for i in range(10): sp = _SP(txn, i) holdme.append(sp) #prevent gc txn._savepoint2index[sp] = i logger._clear() txn.abort() self.assertEqual(list(txn._savepoint2index), []) def test_abort_w_beforeCommitHooks(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction _hooked1, _hooked2 = [], [] def _hook1(*args, **kw): _hooked1.append((args, kw)) def _hook2(*args, **kw): _hooked2.append((args, kw)) logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() txn._before_commit.append((_hook1, ('one',), {'uno': 1})) txn._before_commit.append((_hook2, (), {})) logger._clear() txn.abort() self.assertEqual(_hooked1, []) self.assertEqual(_hooked2, []) # Hooks are neither called nor cleared on abort self.assertEqual(list(txn.getBeforeCommitHooks()), [(_hook1, ('one',), {'uno': 1}), (_hook2, (), {})]) def test_abort_w_synchronizers(self): from transaction.weakset import WeakSet from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction class _Synch(object): _before = _after = False def beforeCompletion(self, txn): self._before = txn def afterCompletion(self, txn): self._after = txn synchs = [_Synch(), _Synch(), _Synch()] ws = WeakSet() for synch in synchs: ws.add(synch) logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne(synchronizers=ws) logger._clear() txn.abort() for synch in synchs: self.assertTrue(synch._before is txn) self.assertTrue(synch._after is txn) def test_abort_w_afterCommitHooks(self): from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction _hooked1, _hooked2 = [], [] def _hook1(*args, **kw): _hooked1.append((args, kw)) def _hook2(*args, **kw): _hooked2.append((args, kw)) logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() txn._after_commit.append((_hook1, ('one',), {'uno': 1})) txn._after_commit.append((_hook2, (), {})) logger._clear() txn.abort() # Hooks are neither called nor cleared on abort self.assertEqual(_hooked1, []) self.assertEqual(_hooked2, []) self.assertEqual(list(txn.getAfterCommitHooks()), [(_hook1, ('one',), {'uno': 1}), (_hook2, (), {})]) def test_abort_error_w_afterCompleteHooks(self): from transaction import _transaction from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey class BrokenResource(object): def sortKey(self): return 'zzz' def abort(self, txn): raise ValueError('test') broken = BrokenResource() resource = Resource('aaa') _hooked1, _hooked2 = [], [] def _hook1(*args, **kw): _hooked1.append((args, kw)) def _hook2(*args, **kw): _hooked2.append((args, kw)) logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): txn = self._makeOne() txn._after_commit.append((_hook1, ('one',), {'uno': 1})) txn._after_commit.append((_hook2, (), {})) txn._resources.append(broken) txn._resources.append(resource) logger._clear() self.assertRaises(ValueError, txn.abort) # Hooks are neither called nor cleared on abort self.assertEqual(_hooked1, []) self.assertEqual(_hooked2, []) self.assertEqual(list(txn.getAfterCommitHooks()), [(_hook1, ('one',), {'uno': 1}), (_hook2, (), {})]) self.assertTrue(resource._a) self.assertFalse(resource._x) def test_abort_error_w_synchronizers(self): from transaction.weakset import WeakSet from transaction.tests.common import DummyLogger from transaction.tests.common import Monkey from transaction import _transaction class _Synch(object): _before = _after = False def beforeCompletion(self, txn): self._before = txn def afterCompletion(self, txn): self._after = txn synchs = [_Synch(), _Synch(), _Synch()] ws = WeakSet() for synch in synchs: ws.add(synch) class BrokenResource(object): def sortKey(self): return 'zzz' def abort(self, txn): raise ValueError('test') broken = BrokenResource() logger = DummyLogger() with Monkey(_transaction, _LOGGER=logger): t = self._makeOne(synchronizers=ws) logger._clear() t._resources.append(broken) self.assertRaises(ValueError, t.abort) for synch in synchs: self.assertTrue(synch._before is t) self.assertTrue(synch._after is t) #called in _cleanup def test_note(self): txn = self._makeOne() try: txn.note('This is a note.') self.assertEqual(txn.description, 'This is a note.') txn.note('Another.') self.assertEqual(txn.description, 'This is a note.\nAnother.') finally: txn.abort() def test_setUser_default_path(self): txn = self._makeOne() txn.setUser('phreddy') self.assertEqual(txn.user, '/ phreddy') def test_setUser_explicit_path(self): txn = self._makeOne() txn.setUser('phreddy', '/bedrock') self.assertEqual(txn.user, '/bedrock phreddy') def test_setExtendedInfo_single(self): txn = self._makeOne() txn.setExtendedInfo('frob', 'qux') self.assertEqual(txn._extension, {'frob': 'qux'}) def test_setExtendedInfo_multiple(self): txn = self._makeOne() txn.setExtendedInfo('frob', 'qux') txn.setExtendedInfo('baz', 'spam') txn.setExtendedInfo('frob', 'quxxxx') self.assertEqual(txn._extension, {'frob': 'quxxxx', 'baz': 'spam'}) class MultiObjectResourceAdapterTests(unittest.TestCase): def _getTargetClass(self): from transaction._transaction import MultiObjectResourceAdapter return MultiObjectResourceAdapter def _makeOne(self, jar): return self._getTargetClass()(jar) def _makeJar(self, key): class _Resource(Resource): def __init__(self, key): super(_Resource, self).__init__(key) self._c = [] self._a = [] def commit(self, obj, txn): self._c.append((obj, txn)) def abort(self, obj, txn): self._a.append((obj, txn)) return _Resource(key) def _makeDummy(self, kind, name): class _Dummy(object): def __init__(self, kind, name): self._kind = kind self._name = name def __repr__(self): return '<%s: %s>' % (self._kind, self._name) return _Dummy(kind, name) def test_ctor(self): jar = self._makeJar('aaa') mora = self._makeOne(jar) self.assertTrue(mora.manager is jar) self.assertEqual(mora.objects, []) self.assertEqual(mora.ncommitted, 0) def test___repr__(self): jar = self._makeJar('bbb') mora = self._makeOne(jar) self.assertEqual(repr(mora), '' % id(mora)) def test_sortKey(self): jar = self._makeJar('ccc') mora = self._makeOne(jar) self.assertEqual(mora.sortKey(), 'ccc') def test_tpc_begin(self): jar = self._makeJar('ddd') mora = self._makeOne(jar) txn = object() mora.tpc_begin(txn) self.assertTrue(jar._b) def test_commit(self): jar = self._makeJar('eee') objects = [self._makeDummy('obj', 'a'), self._makeDummy('obj', 'b')] mora = self._makeOne(jar) mora.objects.extend(objects) txn = self._makeDummy('txn', 'c') mora.commit(txn) self.assertEqual(jar._c, [(objects[0], txn), (objects[1], txn)]) def test_tpc_vote(self): jar = self._makeJar('fff') mora = self._makeOne(jar) txn = object() mora.tpc_vote(txn) self.assertTrue(jar._v) def test_tpc_finish(self): jar = self._makeJar('ggg') mora = self._makeOne(jar) txn = object() mora.tpc_finish(txn) self.assertTrue(jar._f) def test_abort(self): jar = self._makeJar('hhh') objects = [self._makeDummy('obj', 'a'), self._makeDummy('obj', 'b')] mora = self._makeOne(jar) mora.objects.extend(objects) txn = self._makeDummy('txn', 'c') mora.abort(txn) self.assertEqual(jar._a, [(objects[0], txn), (objects[1], txn)]) def test_abort_w_error(self): from transaction.tests.common import DummyLogger jar = self._makeJar('hhh') objects = [self._makeDummy('obj', 'a'), self._makeDummy('obj', 'b'), self._makeDummy('obj', 'c'), ] _old_abort = jar.abort def _abort(obj, txn): if obj._name == 'b': raise ValueError() _old_abort(obj, txn) jar.abort = _abort mora = self._makeOne(jar) mora.objects.extend(objects) txn = self._makeDummy('txn', 'c') txn.log = log = DummyLogger() self.assertRaises(ValueError, mora.abort, txn) self.assertEqual(jar._a, [(objects[0], txn), (objects[2], txn)]) def test_tpc_abort(self): jar = self._makeJar('iii') mora = self._makeOne(jar) txn = object() mora.tpc_abort(txn) self.assertTrue(jar._x) class Test_rm_key(unittest.TestCase): def _callFUT(self, oid): from transaction._transaction import rm_key return rm_key(oid) def test_miss(self): self.assertTrue(self._callFUT(object()) is None) def test_hit(self): self.assertEqual(self._callFUT(Resource('zzz')), 'zzz') class Test_object_hint(unittest.TestCase): def _callFUT(self, oid): from transaction._transaction import object_hint return object_hint(oid) def test_miss(self): class _Test(object): pass test = _Test() self.assertEqual(self._callFUT(test), "_Test oid=None") def test_hit(self): class _Test(object): pass test = _Test() test._p_oid = 'OID' self.assertEqual(self._callFUT(test), "_Test oid='OID'") class Test_oid_repr(unittest.TestCase): def _callFUT(self, oid): from transaction._transaction import oid_repr return oid_repr(oid) def test_as_nonstring(self): self.assertEqual(self._callFUT(123), '123') def test_as_string_not_8_chars(self): self.assertEqual(self._callFUT('a'), "'a'") def test_as_string_z64(self): s = '\0'*8 self.assertEqual(self._callFUT(s), '0x00') def test_as_string_all_Fs(self): s = '\1'*8 self.assertEqual(self._callFUT(s), '0x0101010101010101') class DataManagerAdapterTests(unittest.TestCase): def _getTargetClass(self): from transaction._transaction import DataManagerAdapter return DataManagerAdapter def _makeOne(self, jar): return self._getTargetClass()(jar) def _makeJar(self, key): class _Resource(Resource): _p = False def prepare(self, txn): self._p = True return _Resource(key) def _makeDummy(self, kind, name): class _Dummy(object): def __init__(self, kind, name): self._kind = kind self._name = name def __repr__(self): return '<%s: %s>' % (self._kind, self._name) return _Dummy(kind, name) def test_ctor(self): jar = self._makeJar('aaa') dma = self._makeOne(jar) self.assertTrue(dma._datamanager is jar) def test_commit(self): jar = self._makeJar('bbb') mora = self._makeOne(jar) txn = self._makeDummy('txn', 'c') mora.commit(txn) self.assertFalse(jar._c) #no-op def test_abort(self): jar = self._makeJar('ccc') mora = self._makeOne(jar) txn = self._makeDummy('txn', 'c') mora.abort(txn) self.assertTrue(jar._a) def test_tpc_begin(self): jar = self._makeJar('ddd') mora = self._makeOne(jar) txn = object() mora.tpc_begin(txn) self.assertFalse(jar._b) #no-op def test_tpc_abort(self): jar = self._makeJar('eee') mora = self._makeOne(jar) txn = object() mora.tpc_abort(txn) self.assertFalse(jar._f) self.assertTrue(jar._a) def test_tpc_finish(self): jar = self._makeJar('fff') mora = self._makeOne(jar) txn = object() mora.tpc_finish(txn) self.assertFalse(jar._f) self.assertTrue(jar._c) def test_tpc_vote(self): jar = self._makeJar('ggg') mora = self._makeOne(jar) txn = object() mora.tpc_vote(txn) self.assertFalse(jar._v) self.assertTrue(jar._p) def test_sortKey(self): jar = self._makeJar('hhh') mora = self._makeOne(jar) self.assertEqual(mora.sortKey(), 'hhh') class SavepointTests(unittest.TestCase): def _getTargetClass(self): from transaction._transaction import Savepoint return Savepoint def _makeOne(self, txn, optimistic, *resources): return self._getTargetClass()(txn, optimistic, *resources) def test_ctor_w_savepoint_oblivious_resource_non_optimistic(self): txn = object() resource = object() self.assertRaises(TypeError, self._makeOne, txn, False, resource) def test_ctor_w_savepoint_oblivious_resource_optimistic(self): from transaction._transaction import NoRollbackSavepoint txn = object() resource = object() sp = self._makeOne(txn, True, resource) self.assertEqual(len(sp._savepoints), 1) self.assertTrue(isinstance(sp._savepoints[0], NoRollbackSavepoint)) self.assertTrue(sp._savepoints[0].datamanager is resource) def test_ctor_w_savepoint_aware_resources(self): class _Aware(object): def savepoint(self): return self txn = object() one = _Aware() another = _Aware() sp = self._makeOne(txn, True, one, another) self.assertEqual(len(sp._savepoints), 2) self.assertTrue(isinstance(sp._savepoints[0], _Aware)) self.assertTrue(sp._savepoints[0] is one) self.assertTrue(isinstance(sp._savepoints[1], _Aware)) self.assertTrue(sp._savepoints[1] is another) def test_rollback_w_txn_None(self): from transaction.interfaces import InvalidSavepointRollbackError txn = None class _Aware(object): def savepoint(self): return self resource = _Aware() sp = self._makeOne(txn, False, resource) self.assertRaises(InvalidSavepointRollbackError, sp.rollback) def test_rollback_w_sp_error(self): class _TXN(object): _sarce = False _raia = None def _saveAndRaiseCommitishError(self): import sys from transaction._compat import reraise self._sarce = True reraise(*sys.exc_info()) def _remove_and_invalidate_after(self, sp): self._raia = sp class _Broken(object): def rollback(self): raise ValueError() _broken = _Broken() class _GonnaRaise(object): def savepoint(self): return _broken txn = _TXN() resource = _GonnaRaise() sp = self._makeOne(txn, False, resource) self.assertRaises(ValueError, sp.rollback) self.assertTrue(txn._raia is sp) self.assertTrue(txn._sarce) class AbortSavepointTests(unittest.TestCase): def _getTargetClass(self): from transaction._transaction import AbortSavepoint return AbortSavepoint def _makeOne(self, datamanager, transaction): return self._getTargetClass()(datamanager, transaction) def test_ctor(self): dm = object() txn = object() asp = self._makeOne(dm, txn) self.assertTrue(asp.datamanager is dm) self.assertTrue(asp.transaction is txn) def test_rollback(self): class _DM(object): _aborted = None def abort(self, txn): self._aborted = txn class _TXN(object): _unjoined = None def _unjoin(self, datamanager): self._unjoin = datamanager dm = _DM() txn = _TXN() asp = self._makeOne(dm, txn) asp.rollback() self.assertTrue(dm._aborted is txn) self.assertTrue(txn._unjoin is dm) class NoRollbackSavepointTests(unittest.TestCase): def _getTargetClass(self): from transaction._transaction import NoRollbackSavepoint return NoRollbackSavepoint def _makeOne(self, datamanager): return self._getTargetClass()(datamanager) def test_ctor(self): dm = object() nrsp = self._makeOne(dm) self.assertTrue(nrsp.datamanager is dm) def test_rollback(self): dm = object() nrsp = self._makeOne(dm) self.assertRaises(TypeError, nrsp.rollback) class MiscellaneousTests(unittest.TestCase): def test_BBB_join(self): # The join method is provided for "backward-compatability" with ZODB 4 # data managers. from transaction import Transaction from transaction.tests.examples import DataManager from transaction._transaction import DataManagerAdapter # The argument to join must be a zodb4 data manager, # transaction.interfaces.IDataManager. txn = Transaction() dm = DataManager() txn.join(dm) # The end result is that a data manager adapter is one of the # transaction's objects: self.assertTrue(isinstance(txn._resources[0], DataManagerAdapter)) self.assertTrue(txn._resources[0]._datamanager is dm) def test_bug239086(self): # The original implementation of thread transaction manager made # invalid assumptions about thread ids. import threading import transaction import transaction.tests.savepointsample as SPS dm = SPS.SampleSavepointDataManager() self.assertEqual(list(dm.keys()), []) class Sync: def __init__(self, label): self.label = label self.log = [] def beforeCompletion(self, txn): self.log.append('%s %s' % (self.label, 'before')) def afterCompletion(self, txn): self.log.append('%s %s' % (self.label, 'after')) def newTransaction(self, txn): self.log.append('%s %s' % (self.label, 'new')) def run_in_thread(f): txn = threading.Thread(target=f) txn.start() txn.join() sync = Sync(1) @run_in_thread def first(): transaction.manager.registerSynch(sync) transaction.manager.begin() dm['a'] = 1 self.assertEqual(sync.log, ['1 new']) @run_in_thread def second(): transaction.abort() # should do nothing. self.assertEqual(sync.log, ['1 new']) self.assertEqual(list(dm.keys()), ['a']) dm = SPS.SampleSavepointDataManager() self.assertEqual(list(dm.keys()), []) @run_in_thread def third(): dm['a'] = 1 self.assertEqual(sync.log, ['1 new']) transaction.abort() # should do nothing self.assertEqual(list(dm.keys()), ['a']) class Resource(object): _b = _c = _v = _f = _a = _x = False def __init__(self, key, error=None): self._key = key self._error = error def __repr__(self): return 'Resource: %s' % self._key def sortKey(self): return self._key def tpc_begin(self, txn): if self._error == 'tpc_begin': raise ValueError() self._b = True def commit(self, txn): if self._error == 'commit': raise ValueError() self._c = True def tpc_vote(self, txn): if self._error == 'tpc_vote': raise ValueError() self._v = True def tpc_finish(self, txn): if self._error == 'tpc_finish': raise ValueError() self._f = True def abort(self, txn): if self._error == 'abort': raise ValueError() self._a = True def tpc_abort(self, txn): if self._error == 'tpc_abort': raise ValueError() self._x = True def test_suite(): return unittest.TestSuite(( unittest.makeSuite(TransactionTests), unittest.makeSuite(MultiObjectResourceAdapterTests), unittest.makeSuite(Test_rm_key), unittest.makeSuite(Test_object_hint), unittest.makeSuite(Test_oid_repr), unittest.makeSuite(DataManagerAdapterTests), unittest.makeSuite(SavepointTests), unittest.makeSuite(AbortSavepointTests), unittest.makeSuite(NoRollbackSavepointTests), unittest.makeSuite(MiscellaneousTests), )) transaction-1.4.3/transaction/weakset.py0000664000175000017500000000711612312641051020314 0ustar tseavertseaver############################################################################ # # Copyright (c) 2007 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################ import weakref # A simple implementation of weak sets, supplying just enough of Python's # sets.Set interface for our needs. class WeakSet(object): """A set of objects that doesn't keep its elements alive. The objects in the set must be weakly referencable. The objects need not be hashable, and need not support comparison. Two objects are considered to be the same iff their id()s are equal. When the only references to an object are weak references (including those from WeakSets), the object can be garbage-collected, and will vanish from any WeakSets it may be a member of at that time. """ def __init__(self): # Map id(obj) to obj. By using ids as keys, we avoid requiring # that the elements be hashable or comparable. self.data = weakref.WeakValueDictionary() def __len__(self): return len(self.data) def __contains__(self, obj): return id(obj) in self.data # Same as a Set, add obj to the collection. def add(self, obj): self.data[id(obj)] = obj # Same as a Set, remove obj from the collection, and raise # KeyError if obj not in the collection. def remove(self, obj): del self.data[id(obj)] # f is a one-argument function. Execute f(elt) for each elt in the # set. f's return value is ignored. def map(self, f): for wr in self.as_weakref_list(): elt = wr() if elt is not None: f(elt) # Return a list of weakrefs to all the objects in the collection. # Because a weak dict is used internally, iteration is dicey (the # underlying dict may change size during iteration, due to gc or # activity from other threads). as_weakef_list() is safe. # # Something like this should really be a method of Python's weak dicts. # If we invoke self.data.values() instead, we get back a list of live # objects instead of weakrefs. If gc occurs while this list is alive, # all the objects move to an older generation (because they're strongly # referenced by the list!). They can't get collected then, until a # less frequent collection of the older generation. Before then, if we # invoke self.data.values() again, they're still alive, and if gc occurs # while that list is alive they're all moved to yet an older generation. # And so on. Stress tests showed that it was easy to get into a state # where a WeakSet grows without bounds, despite that almost all its # elements are actually trash. By returning a list of weakrefs instead, # we avoid that, although the decision to use weakrefs is now very # visible to our clients. def as_weakref_list(self): # We're cheating by breaking into the internals of Python's # WeakValueDictionary here (accessing its .data attribute). # Python 3: be sure to freeze the list, to avoid RuntimeError: # dictionary changed size during iteration. return list(self.data.data.values()) transaction-1.4.3/transaction/_transaction.py0000664000175000017500000005577712073316126021363 0ustar tseavertseaver############################################################################ # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################ import binascii import logging import sys import weakref import traceback from zope.interface import implementer from transaction.weakset import WeakSet from transaction.interfaces import TransactionFailedError 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 _marker = object() _TB_BUFFER = None #unittests may hook def _makeTracebackBuffer(): #pragma NO COVER if _TB_BUFFER is not None: return _TB_BUFFER return StringIO() _LOGGER = None #unittests may hook def _makeLogger(): #pragma NO COVER if _LOGGER is not None: return _LOGGER 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: # ACTIVE is the initial state. ACTIVE = "Active" COMMITTING = "Committing" COMMITTED = "Committed" DOOMED = "Doomed" # commit() or commit(True) raised an exception. All further attempts # to commit or join this transaction will raise TransactionFailedError. COMMITFAILED = "Commit failed" @implementer(interfaces.ITransaction, interfaces.ITransactionDeprecated) class Transaction(object): # Assign an index to each savepoint so we can invalidate later savepoints # on rollback. The first index assigned is 1, and it goes up by 1 each # time. _savepoint_index = 0 # If savepoints are used, keep a weak key dict of them. This maps a # savepoint to its index (see above). _savepoint2index = None # Meta data. ._extension is also metadata, but is initialized to an # emtpy dict in __init__. user = "" description = "" def __init__(self, synchronizers=None, manager=None): self.status = Status.ACTIVE # List of resource managers, e.g. MultiObjectResourceAdapters. self._resources = [] # Weak set of synchronizer objects to call. if synchronizers is None: synchronizers = WeakSet() self._synchronizers = synchronizers self._manager = manager # _adapters: Connection/_p_jar -> MultiObjectResourceAdapter[Sub] self._adapters = {} self._voted = {} # id(Connection) -> boolean, True if voted # _voted and other dictionaries use the id() of the resource # manager as a key, because we can't guess whether the actual # resource managers will be safe to use as dict keys. # The user, description, and _extension attributes are accessed # directly by storages, leading underscore notwithstanding. self._extension = {} self.log = _makeLogger() self.log.debug("new transaction") # If a commit fails, the traceback is saved in _failure_traceback. # If another attempt is made to commit, TransactionFailedError is # raised, incorporating this traceback. self._failure_traceback = None # List of (hook, args, kws) tuples added by addBeforeCommitHook(). self._before_commit = [] # List of (hook, args, kws) tuples added by addAfterCommitHook(). self._after_commit = [] def isDoomed(self): """ See ITransaction. """ return self.status is Status.DOOMED def doom(self): """ See ITransaction. """ if self.status is not Status.DOOMED: if self.status is not Status.ACTIVE: # should not doom transactions in the middle, # or after, a commit raise ValueError('non-doomable') self.status = Status.DOOMED # Raise TransactionFailedError, due to commit()/join()/register() # getting called when the current transaction has already suffered # a commit/savepoint failure. def _prior_operation_failed(self): assert self._failure_traceback is not None raise TransactionFailedError("An operation previously failed, " "with traceback:\n\n%s" % self._failure_traceback.getvalue()) def join(self, resource): """ See ITransaction. """ if self.status is Status.COMMITFAILED: self._prior_operation_failed() # doesn't return if (self.status is not Status.ACTIVE and self.status is not Status.DOOMED): # TODO: Should it be possible to join a committing transaction? # 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: # A data manager has joined a transaction *after* a savepoint # was created. A couple of things are different in this case: # # 1. We need to add its savepoint to all previous savepoints. # so that if they are rolled back, we roll this one back too. # # 2. We don't actually need to ask the data manager for a # savepoint: because it's just joining, we can just abort it to # roll back to the current state, so we simply use an # AbortSavepoint. datamanager_savepoint = AbortSavepoint(resource, self) for transaction_savepoint in self._savepoint2index.keys(): transaction_savepoint._savepoints.append( datamanager_savepoint) def _unjoin(self, resource): # Leave a transaction because a savepoint was rolled back on a resource # that joined later. # Don't use remove. We don't want to assume anything about __eq__. self._resources = [r for r in self._resources if r is not resource] def savepoint(self, optimistic=False): """ See ITransaction. """ if self.status is Status.COMMITFAILED: self._prior_operation_failed() # doesn't return, it raises try: savepoint = Savepoint(self, optimistic, *self._resources) except: self._cleanup(self._resources) self._saveAndRaiseCommitishError() # reraises! if self._savepoint2index is None: self._savepoint2index = weakref.WeakKeyDictionary() self._savepoint_index += 1 self._savepoint2index[savepoint] = self._savepoint_index return savepoint # Remove and invalidate all savepoints we know about with an index # larger than `savepoint`'s. This is what's needed when a rollback # _to_ `savepoint` is done. def _remove_and_invalidate_after(self, savepoint): savepoint2index = self._savepoint2index index = savepoint2index[savepoint] # use list(items()) to make copy to avoid mutating while iterating for savepoint, i in list(savepoint2index.items()): if i > index: savepoint.transaction = None # invalidate del savepoint2index[savepoint] # Invalidate and forget about all savepoints. def _invalidate_all_savepoints(self): for savepoint in self._savepoint2index.keys(): 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 persisent 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. """ if self.status is Status.DOOMED: raise interfaces.DoomedTransaction( 'transaction doomed, cannot commit') if self._savepoint2index: self._invalidate_all_savepoints() if self.status is Status.COMMITFAILED: self._prior_operation_failed() # doesn't return self._callBeforeCommitHooks() self._synchronizers.map(lambda s: s.beforeCompletion(self)) self.status = Status.COMMITTING try: self._commitResources() self.status = Status.COMMITTED except: t = None v = None tb = None try: t, v, tb = self._saveAndGetCommitishError() self._callAfterCommitHooks(status=False) reraise(t, v, tb) finally: del t, v, tb else: if self._manager: self._manager.free(self) self._synchronizers.map(lambda s: s.afterCompletion(self)) self._callAfterCommitHooks(status=True) self.log.debug("commit") def _saveAndGetCommitishError(self): self.status = Status.COMMITFAILED # Save the traceback for TransactionFailedError. ft = self._failure_traceback = _makeTracebackBuffer() t = None v = None tb = None try: t, v, tb = sys.exc_info() # Record how we got into commit(). traceback.print_stack(sys._getframe(1), None, ft) # Append the stack entries from here down to the exception. traceback.print_tb(tb, None, ft) # Append the exception type and value. ft.writelines(traceback.format_exception_only(t, v)) return t, v, tb finally: del t, v, tb def _saveAndRaiseCommitishError(self): t = None v = None tb = None try: t, v, tb = self._saveAndGetCommitishError() reraise(t, v, tb) finally: del t, v, tb def getBeforeCommitHooks(self): """ See ITransaction. """ return iter(self._before_commit) def addBeforeCommitHook(self, hook, args=(), kws=None): """ See ITransaction. """ if kws is None: kws = {} self._before_commit.append((hook, tuple(args), kws)) def _callBeforeCommitHooks(self): # Call all hooks registered, allowing further registrations # during processing. Note that calls to addBeforeCommitHook() may # add additional hooks while hooks are running, and iterating over a # growing list is well-defined in Python. for hook, args, kws in self._before_commit: hook(*args, **kws) self._before_commit = [] def getAfterCommitHooks(self): """ See ITransaction. """ return iter(self._after_commit) def addAfterCommitHook(self, hook, args=(), kws=None): """ See ITransaction. """ if kws is None: kws = {} self._after_commit.append((hook, tuple(args), kws)) def _callAfterCommitHooks(self, status=True): # Avoid to abort anything at the end if no hooks are registred. if not self._after_commit: return # Call all hooks registered, allowing further registrations # during processing. Note that calls to addAterCommitHook() may # add additional hooks while hooks are running, and iterating over a # growing list is well-defined in Python. for hook, args, kws in self._after_commit: # The first argument passed to the hook is a Boolean value, # true if the commit succeeded, or false if the commit aborted. try: hook(status, *args, **kws) except: # We need to catch the exceptions if we want all hooks # to be called self.log.error("Error in after commit hook exec in %s ", hook, exc_info=sys.exc_info()) # The transaction is already committed. It must not have # further effects after the commit. for rm in self._resources: try: rm.abort(self) except: # XXX should we take further actions here ? self.log.error("Error in abort() on manager %s", rm, exc_info=sys.exc_info()) self._after_commit = [] self._before_commit = [] def _commitResources(self): # Execute the two-phase commit protocol. L = list(self._resources) L.sort(key=rm_key) try: for rm in L: rm.tpc_begin(self) for rm in L: rm.commit(self) self.log.debug("commit %r" % rm) for rm in L: rm.tpc_vote(self) self._voted[id(rm)] = True try: for rm in L: rm.tpc_finish(self) except: # TODO: do we need to make this warning stronger? # TODO: It would be nice if the system could be configured # to stop committing transactions at this point. self.log.critical("A storage error occurred during the second " "phase of the two-phase commit. Resources " "may be in an inconsistent state.") raise except: # If an error occurs committing a transaction, we try # to revert the changes in each of the resource managers. t, v, tb = sys.exc_info() try: try: self._cleanup(L) finally: self._synchronizers.map(lambda s: s.afterCompletion(self)) reraise(t, v, tb) finally: del t, v, tb def _cleanup(self, L): # Called when an exception occurs during tpc_vote or tpc_finish. for rm in L: if id(rm) not in self._voted: try: rm.abort(self) except Exception: self.log.error("Error in abort() on manager %s", rm, exc_info=sys.exc_info()) for rm in L: try: rm.tpc_abort(self) except Exception: self.log.error("Error in tpc_abort() on manager %s", rm, exc_info=sys.exc_info()) def abort(self): """ See ITransaction. """ if self._savepoint2index: self._invalidate_all_savepoints() self._synchronizers.map(lambda s: s.beforeCompletion(self)) try: t = None v = None tb = None for rm in self._resources: try: rm.abort(self) except: if tb is None: t, v, tb = sys.exc_info() self.log.error("Failed to abort resource manager: %s", rm, exc_info=sys.exc_info()) if self._manager: self._manager.free(self) self._synchronizers.map(lambda s: s.afterCompletion(self)) self.log.debug("abort") if tb is not None: reraise(t, v, tb) finally: del t, v, tb def note(self, text): """ See ITransaction. """ text = text.strip() if self.description: self.description += "\n" + text else: self.description = text def setUser(self, user_name, path="/"): """ See ITransaction. """ self.user = "%s %s" % (path, user_name) def setExtendedInfo(self, name, value): """ See ITransaction. """ self._extension[name] = value # 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 mananger 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: """Transaction savepoint. Transaction savepoints coordinate savepoints for data managers participating in a transaction. """ valid = property(lambda self: self.transaction is not None) def __init__(self, transaction, optimistic, *resources): self.transaction = transaction self._savepoints = savepoints = [] for datamanager in resources: try: savepoint = datamanager.savepoint except AttributeError: if not optimistic: raise TypeError("Savepoints unsupported", datamanager) savepoint = NoRollbackSavepoint(datamanager) else: savepoint = savepoint() savepoints.append(savepoint) def rollback(self): """ See ISavepoint. """ transaction = self.transaction if transaction is None: raise interfaces.InvalidSavepointRollbackError( 'invalidated by a later savepoint') transaction._remove_and_invalidate_after(self) try: for savepoint in self._savepoints: savepoint.rollback() except: # Mark the transaction as failed. transaction._saveAndRaiseCommitishError() # reraises! class AbortSavepoint: def __init__(self, datamanager, transaction): self.datamanager = datamanager self.transaction = transaction def rollback(self): self.datamanager.abort(self.transaction) self.transaction._unjoin(self.datamanager) class NoRollbackSavepoint: def __init__(self, datamanager): self.datamanager = datamanager def rollback(self): raise TypeError("Savepoints unsupported", self.datamanager) transaction-1.4.3/transaction/coverage.xml0000664000175000017500000005110312312641176020617 0ustar tseavertseaver transaction-1.4.3/transaction/nosetests.xml0000664000175000017500000004722712312641176021067 0ustar tseavertseavertransaction-1.4.3/ez_setup.py0000664000175000017500000002140012073316126016160 0ustar tseavertseaver#!python """Bootstrap setuptools installation If you want to use setuptools in your package's setup.py, just include this file in the same directory with it, and add this to the top of your setup.py:: from ez_setup import use_setuptools use_setuptools() If you want to require a specific version of setuptools, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ import sys DEFAULT_VERSION = "0.6c7" DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] md5_data = { 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', } import sys, os def _validate_md5(egg_name, data): if egg_name in md5_data: from md5 import md5 digest = md5(data).hexdigest() if digest != md5_data[egg_name]: print >>sys.stderr, ( "md5 validation of %s failed! (Possible download problem?)" % egg_name ) sys.exit(2) return data def use_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15 ): """Automatically find/download setuptools and make it available on sys.path `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where setuptools will be downloaded, if it is not already available. If `download_delay` is specified, it should be the number of seconds that will be paused before initiating a download, should one be required. If an older version of setuptools is installed, this routine will print a message to ``sys.stderr`` and raise SystemExit in an attempt to abort the calling script. """ try: import setuptools if setuptools.__version__ == '0.0.1': print >>sys.stderr, ( "You have an obsolete version of setuptools installed. Please\n" "remove it from your system entirely before rerunning this script." ) sys.exit(2) except ImportError: egg = download_setuptools(version, download_base, to_dir, download_delay) sys.path.insert(0, egg) import setuptools; setuptools.bootstrap_install_from = egg import pkg_resources try: pkg_resources.require("setuptools>="+version) except pkg_resources.VersionConflict, e: # XXX could we install in a subprocess here? print >>sys.stderr, ( "The required version of setuptools (>=%s) is not available, and\n" "can't be installed while this script is running. Please install\n" " a more recent version first.\n\n(Currently using %r)" ) % (version, e.args[0]) sys.exit(2) def download_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, delay = 15 ): """Download setuptools from a specified location and return its filename `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ import urllib2, shutil egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) url = download_base + egg_name saveto = os.path.join(to_dir, egg_name) src = dst = None if not os.path.exists(saveto): # Avoid repeated downloads try: from distutils import log if delay: log.warn(""" --------------------------------------------------------------------------- This script requires setuptools version %s to run (even to display help). I will attempt to download it for you (from %s), but you may need to enable firewall access for this script first. I will start the download in %d seconds. (Note: if this machine does not have network access, please obtain the file %s and place it in this directory before rerunning this script.) ---------------------------------------------------------------------------""", version, download_base, delay, url ); from time import sleep; sleep(delay) log.warn("Downloading %s", url) src = urllib2.urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = _validate_md5(egg_name, src.read()) dst = open(saveto,"wb"); dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" try: import setuptools except ImportError: egg = None try: egg = download_setuptools(version, delay=0) sys.path.insert(0,egg) from setuptools.command.easy_install import main return main(list(argv)+[egg]) # we're done here finally: if egg and os.path.exists(egg): os.unlink(egg) else: if setuptools.__version__ == '0.0.1': # tell the user to uninstall obsolete version use_setuptools(version) req = "setuptools>="+version import pkg_resources try: pkg_resources.require(req) except pkg_resources.VersionConflict: try: from setuptools.command.easy_install import main except ImportError: from easy_install import main main(list(argv)+[download_setuptools(delay=0)]) sys.exit(0) # try to force an exit else: if argv: from setuptools.command.easy_install import main main(argv) else: print "Setuptools version",version,"or greater has been installed." print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' def update_md5(filenames): """Update our built-in md5 registry""" import re from md5 import md5 for name in filenames: base = os.path.basename(name) f = open(name,'rb') md5_data[base] = md5(f.read()).hexdigest() f.close() data = [" %r: %r,\n" % it for it in md5_data.items()] data.sort() repl = "".join(data) import inspect srcfile = inspect.getsourcefile(sys.modules[__name__]) f = open(srcfile, 'rb'); src = f.read(); f.close() match = re.search("\nmd5_data = {\n([^}]+)}", src) if not match: print >>sys.stderr, "Internal error!" sys.exit(2) src = src[:match.start(1)] + repl + src[match.end(1):] f = open(srcfile,'w') f.write(src) f.close() if __name__=='__main__': if len(sys.argv)>2 and sys.argv[1]=='--md5update': update_md5(sys.argv[2:]) else: main(sys.argv[1:]) transaction-1.4.3/.travis.yml0000664000175000017500000000027512312641051016062 0ustar tseavertseaverlanguage: python python: - 2.6 - 2.7 - 3.2 - 3.3 - pypy install: - pip install . --use-mirrors script: - python setup.py test -q notifications: email: false transaction-1.4.3/COPYRIGHT.txt0000664000175000017500000000004012073316126016056 0ustar tseavertseaverZope Foundation and Contributorstransaction-1.4.3/CHANGES.rst0000664000175000017500000001066612312641256015567 0ustar tseavertseaverChanges ======= 1.4.2 (2014-03-20) ------------------ - Added support for Python 3.4. 1.4.1 (2013-02-20) ------------------ - Document that values returned by ``sortKey`` must be strings, in order to guarantee total ordering. - Fix occasional RuntimeError: dictionary changed size during iteration errors in transaction.weakset on Python 3. 1.4.0 (2013-01-03) ------------------ - Updated Trove classifiers. 1.4.0b1 (2012-12-18) -------------------- - Converted existing doctests into Sphinx documentation (snippets are exercised via 'tox'). - 100% unit test coverage. - Backward incompatibility: raise ValueError rather than AssertionError for runtime errors: - In ``Transaction.doom`` if the transaction is in a non-doomable state. - In ``TransactionManager.attempts`` if passed a non-positive value. - In ``TransactionManager.free`` if passed a foreign transaction. - Declared support for Python 3.3 in ``setup.py``, and added ``tox`` testing. - When a non-retryable exception was raised as the result of a call to ``transaction.manager.commit`` within the "attempts" machinery, the exception was not reraised properly. Symptom: an unrecoverable exception such as ``Unsupported: Storing blobs in is not supported.`` would be swallowed inappropriately. 1.3.0 (2012-05-16) ------------------ - Added Sphinx API docuementation. - Added explicit support for PyPy. - Dropped use of Python3-impatible ``zope.interface.implements`` class advisor in favor of ``zope.interface.implementer`` class decorator. - Added support for continuous integration using ``tox`` and ``jenkins``. - Added ``setup.py docs`` alias (installs ``Sphinx`` and dependencies). - Added ``setup.py dev`` alias (runs ``setup.py develop`` plus installs ``nose`` and ``coverage``). - Python 3.3 compatibility. - Fix "for attempt in transaction.attempts(x)" machinery, which would not retry a transaction if its implicit call to ``.commit()`` itself raised a transient error. Symptom: seeing conflict errors even though you thought you were retrying some number of times via the "attempts" machinery (the first attempt to generate an exception during commit would cause that exception to be raised). 1.2.0 (2011-12-05) ------------------ New Features: - Python 3.2 compatibility. - Dropped Python 2.4 and 2.5 compatibility (use 1.1.1 if you need to use "transaction" under these Python versions). 1.1.1 (2010-09-16) ------------------ Bug Fixes: - Code in ``_transaction.py`` held on to local references to traceback objects after calling ``sys.exc_info()`` to get one, causing potential reference leakages. - Fixed ``hexlify`` NameError in ``transaction._transaction.oid_repr`` and add test. 1.1.0 (1010-05-12) ------------------ New Features: - Transaction managers and the transaction module can be used with the with statement to define transaction boundaries, as in:: with transaction: ... do some things ... See transaction/tests/convenience.txt for more details. - There is a new iterator function that automates dealing with transient errors (such as ZODB confict errors). For example, in:: for attempt in transaction.attempts(5): with attempt: ... do some things .. If the work being done raises transient errors, the transaction will be retried up to 5 times. See transaction/tests/convenience.txt for more details. Bugs fixed: - Fixed a bug that caused extra commit calls to be made on data managers under certain special circumstances. https://mail.zope.org/pipermail/zodb-dev/2010-May/013329.html - When threads were reused, transaction data could leak accross them, causing subtle application bugs. https://bugs.launchpad.net/zodb/+bug/239086 1.0.1 (2010-05-07) ------------------ - LP #142464: remove double newline between log entries: it makes doing smarter formatting harder. - Updated tests to remove use of deprecated ``zope.testing.doctest``. 1.0.0 (2009-07-24) ------------------ - Fix test that incorrectly relied on the order of a list that was generated from a dict. - Remove crufty DEPENDENCIES.cfg left over from zpkg. 1.0a1 (2007-12-18) ------------------ = Initial release, branched from ZODB trunk on 2007-11-08 (aka "3.9.0dev"). - Remove (deprecated) support for beforeCommitHook alias to addBeforeCommitHook. - Add weakset tests. - Remove unit tests that depend on ZODB.tests.utils from test_transaction (these are actually integration tests). transaction-1.4.3/LICENSE.txt0000664000175000017500000000402612073316126015600 0ustar tseavertseaverZope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. transaction-1.4.3/transaction.egg-info/0000775000175000017500000000000012312641352017770 5ustar tseavertseavertransaction-1.4.3/transaction.egg-info/top_level.txt0000664000175000017500000000001412312641350022513 0ustar tseavertseavertransaction transaction-1.4.3/transaction.egg-info/SOURCES.txt0000664000175000017500000000632612312641352021663 0ustar tseavertseaver.gitignore .travis.yml CHANGES.rst COPYRIGHT.txt LICENSE.txt MANIFEST.in README.rst bootstrap.py buildout.cfg ez_setup.py setup.cfg setup.py tox.ini docs/Makefile docs/api.rst docs/conf.py docs/convenience.rst docs/datamanager.rst docs/doom.rst docs/hooks.rst docs/index.rst docs/make.bat docs/resourcemanager.rst docs/savepoint.rst docs/_build/doctest/output.txt docs/_build/doctrees/api.doctree docs/_build/doctrees/convenience.doctree docs/_build/doctrees/datamanager.doctree docs/_build/doctrees/doom.doctree docs/_build/doctrees/environment.pickle docs/_build/doctrees/hooks.doctree docs/_build/doctrees/index.doctree docs/_build/doctrees/resourcemanager.doctree docs/_build/doctrees/savepoint.doctree docs/_build/html/.buildinfo docs/_build/html/api.html docs/_build/html/convenience.html docs/_build/html/datamanager.html docs/_build/html/doom.html docs/_build/html/genindex.html docs/_build/html/hooks.html docs/_build/html/index.html docs/_build/html/objects.inv docs/_build/html/py-modindex.html docs/_build/html/resourcemanager.html docs/_build/html/savepoint.html docs/_build/html/search.html docs/_build/html/searchindex.js docs/_build/html/_modules/index.html docs/_build/html/_modules/transaction/_manager.html docs/_build/html/_modules/transaction/_transaction.html docs/_build/html/_modules/transaction/interfaces.html docs/_build/html/_sources/api.txt docs/_build/html/_sources/convenience.txt docs/_build/html/_sources/datamanager.txt docs/_build/html/_sources/doom.txt docs/_build/html/_sources/hooks.txt docs/_build/html/_sources/index.txt docs/_build/html/_sources/resourcemanager.txt docs/_build/html/_sources/savepoint.txt docs/_build/html/_static/ajax-loader.gif docs/_build/html/_static/basic.css docs/_build/html/_static/comment-bright.png docs/_build/html/_static/comment-close.png docs/_build/html/_static/comment.png docs/_build/html/_static/default.css docs/_build/html/_static/doctools.js docs/_build/html/_static/down-pressed.png docs/_build/html/_static/down.png docs/_build/html/_static/file.png docs/_build/html/_static/jquery.js docs/_build/html/_static/minus.png docs/_build/html/_static/placeholder.txt docs/_build/html/_static/plus.png docs/_build/html/_static/pygments.css docs/_build/html/_static/searchtools.js docs/_build/html/_static/sidebar.js docs/_build/html/_static/underscore.js docs/_build/html/_static/up-pressed.png docs/_build/html/_static/up.png docs/_build/html/_static/websupport.js docs/_static/placeholder.txt docs/_templates/placeholder.txt transaction/__init__.py transaction/_compat.py transaction/_manager.py transaction/_transaction.py transaction/coverage.xml transaction/interfaces.py transaction/nosetests.xml transaction/weakset.py transaction.egg-info/PKG-INFO transaction.egg-info/SOURCES.txt transaction.egg-info/dependency_links.txt transaction.egg-info/entry_points.txt transaction.egg-info/not-zip-safe transaction.egg-info/requires.txt transaction.egg-info/top_level.txt transaction/tests/__init__.py transaction/tests/common.py transaction/tests/examples.py transaction/tests/savepointsample.py transaction/tests/test__manager.py transaction/tests/test__transaction.py transaction/tests/test_register_compat.py transaction/tests/test_savepoint.py transaction/tests/test_weakset.py transaction/tests/warnhook.pytransaction-1.4.3/transaction.egg-info/PKG-INFO0000664000175000017500000001621712312641350021072 0ustar tseavertseaverMetadata-Version: 1.0 Name: transaction Version: 1.4.3 Summary: Transaction management for Python Home-page: http://www.zope.org/Products/ZODB Author: Zope Corporation Author-email: zodb-dev@zope.org License: ZPL 2.1 Description: ============ Transactions ============ This package contains a generic transaction implementation for Python. It is mainly used by the ZODB. See http://zodb.readthedocs.org/en/latest/transactions.html for narrative documentation on its usage. Changes ======= 1.4.2 (2014-03-20) ------------------ - Added support for Python 3.4. 1.4.1 (2013-02-20) ------------------ - Document that values returned by ``sortKey`` must be strings, in order to guarantee total ordering. - Fix occasional RuntimeError: dictionary changed size during iteration errors in transaction.weakset on Python 3. 1.4.0 (2013-01-03) ------------------ - Updated Trove classifiers. 1.4.0b1 (2012-12-18) -------------------- - Converted existing doctests into Sphinx documentation (snippets are exercised via 'tox'). - 100% unit test coverage. - Backward incompatibility: raise ValueError rather than AssertionError for runtime errors: - In ``Transaction.doom`` if the transaction is in a non-doomable state. - In ``TransactionManager.attempts`` if passed a non-positive value. - In ``TransactionManager.free`` if passed a foreign transaction. - Declared support for Python 3.3 in ``setup.py``, and added ``tox`` testing. - When a non-retryable exception was raised as the result of a call to ``transaction.manager.commit`` within the "attempts" machinery, the exception was not reraised properly. Symptom: an unrecoverable exception such as ``Unsupported: Storing blobs in is not supported.`` would be swallowed inappropriately. 1.3.0 (2012-05-16) ------------------ - Added Sphinx API docuementation. - Added explicit support for PyPy. - Dropped use of Python3-impatible ``zope.interface.implements`` class advisor in favor of ``zope.interface.implementer`` class decorator. - Added support for continuous integration using ``tox`` and ``jenkins``. - Added ``setup.py docs`` alias (installs ``Sphinx`` and dependencies). - Added ``setup.py dev`` alias (runs ``setup.py develop`` plus installs ``nose`` and ``coverage``). - Python 3.3 compatibility. - Fix "for attempt in transaction.attempts(x)" machinery, which would not retry a transaction if its implicit call to ``.commit()`` itself raised a transient error. Symptom: seeing conflict errors even though you thought you were retrying some number of times via the "attempts" machinery (the first attempt to generate an exception during commit would cause that exception to be raised). 1.2.0 (2011-12-05) ------------------ New Features: - Python 3.2 compatibility. - Dropped Python 2.4 and 2.5 compatibility (use 1.1.1 if you need to use "transaction" under these Python versions). 1.1.1 (2010-09-16) ------------------ Bug Fixes: - Code in ``_transaction.py`` held on to local references to traceback objects after calling ``sys.exc_info()`` to get one, causing potential reference leakages. - Fixed ``hexlify`` NameError in ``transaction._transaction.oid_repr`` and add test. 1.1.0 (1010-05-12) ------------------ New Features: - Transaction managers and the transaction module can be used with the with statement to define transaction boundaries, as in:: with transaction: ... do some things ... See transaction/tests/convenience.txt for more details. - There is a new iterator function that automates dealing with transient errors (such as ZODB confict errors). For example, in:: for attempt in transaction.attempts(5): with attempt: ... do some things .. If the work being done raises transient errors, the transaction will be retried up to 5 times. See transaction/tests/convenience.txt for more details. Bugs fixed: - Fixed a bug that caused extra commit calls to be made on data managers under certain special circumstances. https://mail.zope.org/pipermail/zodb-dev/2010-May/013329.html - When threads were reused, transaction data could leak accross them, causing subtle application bugs. https://bugs.launchpad.net/zodb/+bug/239086 1.0.1 (2010-05-07) ------------------ - LP #142464: remove double newline between log entries: it makes doing smarter formatting harder. - Updated tests to remove use of deprecated ``zope.testing.doctest``. 1.0.0 (2009-07-24) ------------------ - Fix test that incorrectly relied on the order of a list that was generated from a dict. - Remove crufty DEPENDENCIES.cfg left over from zpkg. 1.0a1 (2007-12-18) ------------------ = Initial release, branched from ZODB trunk on 2007-11-08 (aka "3.9.0dev"). - Remove (deprecated) support for beforeCommitHook alias to addBeforeCommitHook. - Add weakset tests. - Remove unit tests that depend on ZODB.tests.utils from test_transaction (these are actually integration tests). Platform: any Classifier: Development Status :: 6 - Mature Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Topic :: Database Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: Unix Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Framework :: ZODB transaction-1.4.3/transaction.egg-info/dependency_links.txt0000664000175000017500000000000112312641350024034 0ustar tseavertseaver transaction-1.4.3/transaction.egg-info/not-zip-safe0000664000175000017500000000000112074035504022217 0ustar tseavertseaver transaction-1.4.3/transaction.egg-info/requires.txt0000664000175000017500000000012212312641350022361 0ustar tseavertseaverzope.interface [docs] Sphinx repoze.sphinx.autointerface [testing] nose coveragetransaction-1.4.3/transaction.egg-info/entry_points.txt0000664000175000017500000000000612312641350023260 0ustar tseavertseaver transaction-1.4.3/README.rst0000664000175000017500000000036612312641051015441 0ustar tseavertseaver============ Transactions ============ This package contains a generic transaction implementation for Python. It is mainly used by the ZODB. See http://zodb.readthedocs.org/en/latest/transactions.html for narrative documentation on its usage. transaction-1.4.3/docs/0000775000175000017500000000000012312641352014701 5ustar tseavertseavertransaction-1.4.3/docs/resourcemanager.rst0000664000175000017500000001757012073316126020631 0ustar tseavertseaverWriting a Resource Manager ========================== Simple Resource Manager ----------------------- .. doctest:: >>> from transaction.tests.examples import ResourceManager This :class:`transaction.tests.examples.ResourceManager` class provides a trivial resource-manager implementation and doc strings to illustrate the protocol and to provide a tool for writing tests. Our sample resource manager has state that is updated through an inc method and through transaction operations. When we create a sample resource manager: .. doctest:: >>> rm = ResourceManager() It has two pieces state, state and delta, both initialized to 0: .. doctest:: >>> rm.state 0 >>> rm.delta 0 state is meant to model committed state, while delta represents tentative changes within a transaction. We change the state by calling inc: .. doctest:: >>> rm.inc() which updates delta: .. doctest:: >>> rm.delta 1 but state isn't changed until we commit the transaction: .. doctest:: >>> rm.state 0 To commit the changes, we use 2-phase commit. We execute the first stage by calling prepare. We need to pass a transation. Our sample resource managers don't really use the transactions for much, so we'll be lazy and use strings for transactions. The sample resource manager updates the state when we call tpc_vote: .. doctest:: >>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.state, rm.delta (0, 1) >>> rm.tpc_vote(t1) >>> rm.state, rm.delta (1, 1) Now if we call tpc_finish: >>> rm.tpc_finish(t1) Our changes are "permanent". The state reflects the changes and the delta has been reset to 0. .. doctest:: >>> rm.state, rm.delta (1, 0) The :meth:`tpc_begin` Method ----------------------------- Called by the transaction manager to ask the RM to prepare to commit data. .. doctest:: >>> rm = ResourceManager() >>> rm.inc() >>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> rm.state 1 >>> rm.inc() >>> t2 = '2' >>> rm.tpc_begin(t2) >>> rm.tpc_vote(t2) >>> rm.tpc_abort(t2) >>> rm.state 1 It is an error to call tpc_begin more than once without completing two-phase commit: .. doctest:: >>> rm.tpc_begin(t1) >>> rm.tpc_begin(t1) Traceback (most recent call last): ... ValueError: txn in state 'tpc_begin' but expected one of (None,) >>> rm.tpc_abort(t1) If there was a preceeding savepoint, the transaction must match: .. doctest:: >>> rollback = rm.savepoint(t1) >>> rm.tpc_begin(t2) Traceback (most recent call last): ,,, TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_begin(t1) The :meth:`tpc_vote` Method --------------------------- Verify that a data manager can commit the transaction. This is the last chance for a data manager to vote 'no'. A data manager votes 'no' by raising an exception. Passed `transaction`, which is the ITransaction instance associated with the transaction being committed. The :meth:`tpc_finish` Method ----------------------------- Complete two-phase commit .. doctest:: >>> rm = ResourceManager() >>> rm.state 0 >>> rm.inc() We start two-phase commit by calling prepare: >>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) We complete it by calling tpc_finish: >>> rm.tpc_finish(t1) >>> rm.state 1 It is an error ro call tpc_finish without calling tpc_vote: .. doctest:: >>> rm.inc() >>> t2 = '2' >>> rm.tpc_begin(t2) >>> rm.tpc_finish(t2) Traceback (most recent call last): ... ValueError: txn in state 'tpc_begin' but expected one of ('tpc_vote',) >>> rm.tpc_abort(t2) # clean slate >>> rm.tpc_begin(t2) >>> rm.tpc_vote(t2) >>> rm.tpc_finish(t2) Of course, the transactions given to tpc_begin and tpc_finish must be the same: .. doctest:: >>> rm.inc() >>> t3 = '3' >>> rm.tpc_begin(t3) >>> rm.tpc_vote(t3) >>> rm.tpc_finish(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '3') The :meth:`tpc_abort` Method ----------------------------- Abort a transaction The abort method can be called before two-phase commit to throw away work done in the transaction: .. doctest:: >>> rm = ResourceManager() >>> rm.inc() >>> rm.state, rm.delta (0, 1) >>> t1 = '1' >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0) The abort method also throws away work done in savepoints: .. doctest:: >>> rm.inc() >>> r = rm.savepoint(t1) >>> rm.inc() >>> r = rm.savepoint(t1) >>> rm.state, rm.delta (0, 2) >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0) If savepoints are used, abort must be passed the same transaction: .. doctest:: >>> rm.inc() >>> r = rm.savepoint(t1) >>> t2 = '2' >>> rm.tpc_abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_abort(t1) The abort method is also used to abort a two-phase commit: .. doctest:: >>> rm.inc() >>> rm.state, rm.delta (0, 1) >>> rm.tpc_begin(t1) >>> rm.state, rm.delta (0, 1) >>> rm.tpc_vote(t1) >>> rm.state, rm.delta (1, 1) >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0) Of course, the transactions passed to prepare and abort must match: .. doctest:: >>> rm.tpc_begin(t1) >>> rm.tpc_abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_abort(t1) This should never fail. The :meth:`savepoint` Method ---------------------------- Provide the ability to rollback transaction state Savepoints provide a way to: - Save partial transaction work. For some resource managers, this could allow resources to be used more efficiently. - Provide the ability to revert state to a point in a transaction without aborting the entire transaction. In other words, savepoints support partial aborts. Savepoints don't use two-phase commit. If there are errors in setting or rolling back to savepoints, the application should abort the containing transaction. This is *not* the responsibility of the resource manager. Savepoints are always associated with a transaction. Any work done in a savepoint's transaction is tentative until the transaction is committed using two-phase commit. .. doctest:: >>> rm = ResourceManager() >>> rm.inc() >>> t1 = '1' >>> r = rm.savepoint(t1) >>> rm.state, rm.delta (0, 1) >>> rm.inc() >>> rm.state, rm.delta (0, 2) >>> r.rollback() >>> rm.state, rm.delta (0, 1) >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> rm.state, rm.delta (1, 0) Savepoints must have the same transaction: .. doctest:: >>> r1 = rm.savepoint(t1) >>> rm.state, rm.delta (1, 0) >>> rm.inc() >>> rm.state, rm.delta (1, 1) >>> t2 = '2' >>> r2 = rm.savepoint(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> r2 = rm.savepoint(t1) >>> rm.inc() >>> rm.state, rm.delta (1, 2) If we rollback to an earlier savepoint, we discard all work done later: .. doctest:: >>> r1.rollback() >>> rm.state, rm.delta (1, 0) and we can no longer rollback to the later savepoint: .. doctest:: >>> r2.rollback() Traceback (most recent call last): ... TypeError: ('Attempt to roll back to invalid save point', 3, 2) We can roll back to a savepoint as often as we like: .. doctest:: >>> r1.rollback() >>> r1.rollback() >>> r1.rollback() >>> rm.state, rm.delta (1, 0) >>> rm.inc() >>> rm.inc() >>> rm.inc() >>> rm.state, rm.delta (1, 3) >>> r1.rollback() >>> rm.state, rm.delta (1, 0) But we can't rollback to a savepoint after it has been committed: .. doctest:: >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> r1.rollback() Traceback (most recent call last): ... TypeError: Attempt to rollback stale rollback transaction-1.4.3/docs/api.rst0000664000175000017500000000306412073316126016211 0ustar tseavertseaver:mod:`transaction` API Reference ================================ Interfaces ---------- .. module:: transaction.interfaces .. autointerface:: ITransactionManager :members: :member-order: bysource .. autointerface:: ITransaction :members: :member-order: bysource .. autointerface:: IDataManager :members: :member-order: bysource .. autointerface:: ISavepointDataManager :members: :member-order: bysource .. autointerface:: IDataManagerSavepoint :members: :member-order: bysource .. autointerface:: ISavepoint :members: :member-order: bysource .. autoclass:: InvalidSavepointRollbackError :members: :member-order: bysource .. autointerface:: ISynchronizer :members: :member-order: bysource .. autoclass:: TransactionError :members: :member-order: bysource .. autoclass:: TransactionFailedError :members: :member-order: bysource .. autoclass:: DoomedTransaction :members: :member-order: bysource .. autoclass:: TransientError :members: :member-order: bysource API Objects ----------- .. module:: transaction._transaction .. autoclass:: Transaction :members: :member-order: bysource .. autoclass:: Savepoint :members: :member-order: bysource .. module:: transaction._manager .. autoclass:: TransactionManager :members: :member-order: bysource .. automethod:: __enter__ Alias for :meth:`get` .. automethod:: __exit__ On error, aborts the current transaction. Otherwise, commits. .. autoclass:: ThreadTransactionManager :members: :member-order: bysource transaction-1.4.3/docs/make.bat0000664000175000017500000001176212073316126016317 0ustar tseavertseaver@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\transaction.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\transaction.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end transaction-1.4.3/docs/_templates/0000775000175000017500000000000012312641352017036 5ustar tseavertseavertransaction-1.4.3/docs/_templates/placeholder.txt0000664000175000017500000000000012073316126022051 0ustar tseavertseavertransaction-1.4.3/docs/doom.rst0000664000175000017500000001050012073316126016367 0ustar tseavertseaverDooming Transactions ==================== A doomed transaction behaves exactly the same way as an active transaction but raises an error on any attempt to commit it, thus forcing an abort. Doom is useful in places where abort is unsafe and an exception cannot be raised. This occurs when the programmer wants the code following the doom to run but not commit. It is unsafe to abort in these circumstances as a following get() may implicitly open a new transaction. Any attempt to commit a doomed transaction will raise a DoomedTransaction exception. An example of such a use case can be found in zope/app/form/browser/editview.py. Here a form validation failure must doom the transaction as committing the transaction may have side-effects. However, the form code must continue to calculate a form containing the error messages to return. For Zope in general, code running within a request should always doom transactions rather than aborting them. It is the responsibilty of the publication to either abort() or commit() the transaction. Application code can use savepoints and doom() safely. To see how it works we first need to create a stub data manager: .. doctest:: >>> from transaction.interfaces import IDataManager >>> from zope.interface import implementer >>> @implementer(IDataManager) ... class DataManager: ... def __init__(self): ... self.attr_counter = {} ... def __getattr__(self, name): ... def f(transaction): ... self.attr_counter[name] = self.attr_counter.get(name, 0) + 1 ... return f ... def total(self): ... count = 0 ... for access_count in self.attr_counter.values(): ... count += access_count ... return count ... def sortKey(self): ... return 1 Start a new transaction: .. doctest:: >>> import transaction >>> txn = transaction.begin() >>> dm = DataManager() >>> txn.join(dm) We can ask a transaction if it is doomed to avoid expensive operations. An example of a use case is an object-relational mapper where a pre-commit hook sends all outstanding SQL to a relational database for objects changed during the transaction. This expensive operation is not necessary if the transaction has been doomed. A non-doomed transaction should return False: .. doctest:: >>> txn.isDoomed() False We can doom a transaction by calling .doom() on it: .. doctest:: >>> txn.doom() >>> txn.isDoomed() True We can doom it again if we like: .. doctest:: >>> txn.doom() The data manager is unchanged at this point: .. doctest:: >>> dm.total() 0 Attempting to commit a doomed transaction any number of times raises a DoomedTransaction: .. doctest:: >>> txn.commit() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): DoomedTransaction: transaction doomed, cannot commit >>> txn.commit() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): DoomedTransaction: transaction doomed, cannot commit But still leaves the data manager unchanged: .. doctest:: >>> dm.total() 0 But the doomed transaction can be aborted: .. doctest:: >>> txn.abort() Which aborts the data manager: .. doctest:: >>> dm.total() 1 >>> dm.attr_counter['abort'] 1 Dooming the current transaction can also be done directly from the transaction module. We can also begin a new transaction directly after dooming the old one: .. doctest:: >>> txn = transaction.begin() >>> transaction.isDoomed() False >>> transaction.doom() >>> transaction.isDoomed() True >>> txn = transaction.begin() After committing a transaction we get an assertion error if we try to doom the transaction. This could be made more specific, but trying to doom a transaction after it's been committed is probably a programming error: .. doctest:: >>> txn = transaction.begin() >>> txn.commit() >>> txn.doom() Traceback (most recent call last): ... ValueError: non-doomable A doomed transaction should act the same as an active transaction, so we should be able to join it: .. doctest:: >>> txn = transaction.begin() >>> txn.doom() >>> dm2 = DataManager() >>> txn.join(dm2) Clean up: .. doctest:: >>> txn = transaction.begin() >>> txn.abort() transaction-1.4.3/docs/Makefile0000664000175000017500000001272012073316126016345 0ustar tseavertseaver# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/transaction.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/transaction.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/transaction" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/transaction" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." transaction-1.4.3/docs/hooks.rst0000664000175000017500000002257312073316126016571 0ustar tseavertseaverHooking the Transaction Machinery ================================= The :meth:`addBeforeCommitHook` Method -------------------------------------- Let's define a hook to call, and a way to see that it was called. .. doctest:: >>> log = [] >>> def reset_log(): ... del log[:] >>> def hook(arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... log.append("arg %r kw1 %r kw2 %r" % (arg, kw1, kw2)) Now register the hook with a transaction. .. doctest:: >>> from transaction import begin >>> from transaction._compat import func_name >>> import transaction >>> t = begin() >>> t.addBeforeCommitHook(hook, '1') We can see that the hook is indeed registered. .. doctest:: >>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getBeforeCommitHooks()] [('hook', ('1',), {})] When transaction commit starts, the hook is called, with its arguments. .. doctest:: >>> log [] >>> t.commit() >>> log ["arg '1' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log() A hook's registration is consumed whenever the hook is called. Since the hook above was called, it's no longer registered: .. doctest:: >>> from transaction import commit >>> len(list(t.getBeforeCommitHooks())) 0 >>> commit() >>> log [] The hook is only called for a full commit, not for a savepoint. .. doctest:: >>> t = begin() >>> t.addBeforeCommitHook(hook, 'A', dict(kw1='B')) >>> dummy = t.savepoint() >>> log [] >>> t.commit() >>> log ["arg 'A' kw1 'B' kw2 'no_kw2'"] >>> reset_log() If a transaction is aborted, no hook is called. .. doctest:: >>> from transaction import abort >>> t = begin() >>> t.addBeforeCommitHook(hook, ["OOPS!"]) >>> abort() >>> log [] >>> commit() >>> log [] The hook is called before the commit does anything, so even if the commit fails the hook will have been called. To provoke failures in commit, we'll add failing resource manager to the transaction. .. doctest:: >>> class CommitFailure(Exception): ... pass >>> class FailingDataManager: ... def tpc_begin(self, txn, sub=False): ... raise CommitFailure('failed') ... def abort(self, txn): ... pass >>> t = begin() >>> t.join(FailingDataManager()) >>> t.addBeforeCommitHook(hook, '2') >>> from transaction.tests.common import DummyFile >>> from transaction.tests.common import Monkey >>> from transaction.tests.common import assertRaisesEx >>> from transaction import _transaction >>> buffer = DummyFile() >>> with Monkey(_transaction, _TB_BUFFER=buffer): ... err = assertRaisesEx(CommitFailure, t.commit) >>> log ["arg '2' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log() Let's register several hooks. .. doctest:: >>> t = begin() >>> t.addBeforeCommitHook(hook, '4', dict(kw1='4.1')) >>> t.addBeforeCommitHook(hook, '5', dict(kw2='5.2')) They are returned in the same order by getBeforeCommitHooks. .. doctest:: >>> [(func_name(hook), args, kws) #doctest: +NORMALIZE_WHITESPACE ... for hook, args, kws in t.getBeforeCommitHooks()] [('hook', ('4',), {'kw1': '4.1'}), ('hook', ('5',), {'kw2': '5.2'})] And commit also calls them in this order. .. doctest:: >>> t.commit() >>> len(log) 2 >>> log #doctest: +NORMALIZE_WHITESPACE ["arg '4' kw1 '4.1' kw2 'no_kw2'", "arg '5' kw1 'no_kw1' kw2 '5.2'"] >>> reset_log() While executing, a hook can itself add more hooks, and they will all be called before the real commit starts. .. doctest:: >>> def recurse(txn, arg): ... log.append('rec' + str(arg)) ... if arg: ... txn.addBeforeCommitHook(hook, '-') ... txn.addBeforeCommitHook(recurse, (txn, arg-1)) >>> t = begin() >>> t.addBeforeCommitHook(recurse, (t, 3)) >>> commit() >>> log #doctest: +NORMALIZE_WHITESPACE ['rec3', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec2', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec1', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec0'] >>> reset_log() The :meth:`addAfterCommitHook` Method -------------------------------------- Let's define a hook to call, and a way to see that it was called. .. doctest:: >>> log = [] >>> def reset_log(): ... del log[:] >>> def hook(status, arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... log.append("%r arg %r kw1 %r kw2 %r" % (status, arg, kw1, kw2)) Now register the hook with a transaction. .. doctest:: >>> from transaction import begin >>> from transaction._compat import func_name >>> t = begin() >>> t.addAfterCommitHook(hook, '1') We can see that the hook is indeed registered. .. doctest:: >>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getAfterCommitHooks()] [('hook', ('1',), {})] When transaction commit is done, the hook is called, with its arguments. .. doctest:: >>> log [] >>> t.commit() >>> log ["True arg '1' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log() A hook's registration is consumed whenever the hook is called. Since the hook above was called, it's no longer registered: .. doctest:: >>> from transaction import commit >>> len(list(t.getAfterCommitHooks())) 0 >>> commit() >>> log [] The hook is only called after a full commit, not for a savepoint. .. doctest:: >>> t = begin() >>> t.addAfterCommitHook(hook, 'A', dict(kw1='B')) >>> dummy = t.savepoint() >>> log [] >>> t.commit() >>> log ["True arg 'A' kw1 'B' kw2 'no_kw2'"] >>> reset_log() If a transaction is aborted, no hook is called. .. doctest:: >>> from transaction import abort >>> t = begin() >>> t.addAfterCommitHook(hook, ["OOPS!"]) >>> abort() >>> log [] >>> commit() >>> log [] The hook is called after the commit is done, so even if the commit fails the hook will have been called. To provoke failures in commit, we'll add failing resource manager to the transaction. .. doctest:: >>> class CommitFailure(Exception): ... pass >>> class FailingDataManager: ... def tpc_begin(self, txn): ... raise CommitFailure('failed') ... def abort(self, txn): ... pass >>> t = begin() >>> t.join(FailingDataManager()) >>> t.addAfterCommitHook(hook, '2') >>> from transaction.tests.common import DummyFile >>> from transaction.tests.common import Monkey >>> from transaction.tests.common import assertRaisesEx >>> from transaction import _transaction >>> buffer = DummyFile() >>> with Monkey(_transaction, _TB_BUFFER=buffer): ... err = assertRaisesEx(CommitFailure, t.commit) >>> log ["False arg '2' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log() Let's register several hooks. .. doctest:: >>> t = begin() >>> t.addAfterCommitHook(hook, '4', dict(kw1='4.1')) >>> t.addAfterCommitHook(hook, '5', dict(kw2='5.2')) They are returned in the same order by getAfterCommitHooks. .. doctest:: >>> [(func_name(hook), args, kws) #doctest: +NORMALIZE_WHITESPACE ... for hook, args, kws in t.getAfterCommitHooks()] [('hook', ('4',), {'kw1': '4.1'}), ('hook', ('5',), {'kw2': '5.2'})] And commit also calls them in this order. .. doctest:: >>> t.commit() >>> len(log) 2 >>> log #doctest: +NORMALIZE_WHITESPACE ["True arg '4' kw1 '4.1' kw2 'no_kw2'", "True arg '5' kw1 'no_kw1' kw2 '5.2'"] >>> reset_log() While executing, a hook can itself add more hooks, and they will all be called before the real commit starts. .. doctest:: >>> def recurse(status, txn, arg): ... log.append('rec' + str(arg)) ... if arg: ... txn.addAfterCommitHook(hook, '-') ... txn.addAfterCommitHook(recurse, (txn, arg-1)) >>> t = begin() >>> t.addAfterCommitHook(recurse, (t, 3)) >>> commit() >>> log #doctest: +NORMALIZE_WHITESPACE ['rec3', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec2', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec1', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec0'] >>> reset_log() If an after commit hook is raising an exception then it will log a message at error level so that if other hooks are registered they can be executed. We don't support execution dependencies at this level. .. doctest:: >>> from transaction import TransactionManager >>> from transaction.tests.test__manager import DataObject >>> mgr = TransactionManager() >>> do = DataObject(mgr) >>> def hookRaise(status, arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... raise TypeError("Fake raise") >>> t = begin() >>> t.addAfterCommitHook(hook, ('-', 1)) >>> t.addAfterCommitHook(hookRaise, ('-', 2)) >>> t.addAfterCommitHook(hook, ('-', 3)) >>> commit() >>> log ["True arg '-' kw1 1 kw2 'no_kw2'", "True arg '-' kw1 3 kw2 'no_kw2'"] >>> reset_log() Test that the associated transaction manager has been cleanup when after commit hooks are registered .. doctest:: >>> mgr = TransactionManager() >>> do = DataObject(mgr) >>> t = begin() >>> t._manager._txn is not None True >>> t.addAfterCommitHook(hook, ('-', 1)) >>> commit() >>> log ["True arg '-' kw1 1 kw2 'no_kw2'"] >>> t._manager._txn is not None False >>> reset_log() transaction-1.4.3/docs/_static/0000775000175000017500000000000012312641352016327 5ustar tseavertseavertransaction-1.4.3/docs/_static/placeholder.txt0000664000175000017500000000000012073316126021342 0ustar tseavertseavertransaction-1.4.3/docs/conf.py0000664000175000017500000001743412073316126016213 0ustar tseavertseaver# -*- coding: utf-8 -*- # # transaction documentation build configuration file, created by # sphinx-quickstart on Wed May 16 16:43:53 2012. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.viewcode', 'repoze.sphinx.autointerface', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'transaction' copyright = u'2012, Zope Foundation Contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.2' # The full version, including alpha/beta/rc tags. release = '1.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'transactiondoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'transaction.tex', u'transaction Documentation', u'Zope Foundation Contributors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'transaction', u'transaction Documentation', [u'Zope Foundation Contributors'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'transaction', u'transaction Documentation', u'Zope Foundation Contributors', 'transaction', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' transaction-1.4.3/docs/savepoint.rst0000664000175000017500000002071712073316126017454 0ustar tseavertseaverSavepoints ========== Savepoints provide a way to save to disk intermediate work done during a transaction allowing: - partial transaction (subtransaction) rollback (abort) - state of saved objects to be freed, freeing on-line memory for other uses Savepoints make it possible to write atomic subroutines that don't make top-level transaction commitments. Applications ------------ To demonstrate how savepoints work with transactions, we've provided a sample data manager implementation that provides savepoint support. The primary purpose of this data manager is to provide code that can be read to understand how savepoints work. The secondary purpose is to provide support for demonstrating the correct operation of savepoint support within the transaction system. This data manager is very simple. It provides flat storage of named immutable values, like strings and numbers. .. doctest:: >>> import transaction >>> from transaction.tests import savepointsample >>> dm = savepointsample.SampleSavepointDataManager() >>> dm['name'] = 'bob' As with other data managers, we can commit changes: .. doctest:: >>> transaction.commit() >>> dm['name'] 'bob' and abort changes: .. doctest:: >>> dm['name'] = 'sally' >>> dm['name'] 'sally' >>> transaction.abort() >>> dm['name'] 'bob' Now, let's look at an application that manages funds for people. It allows deposits and debits to be entered for multiple people. It accepts a sequence of entries and generates a sequence of status messages. For each entry, it applies the change and then validates the user's account. If the user's account is invalid, we roll back the change for that entry. The success or failure of an entry is indicated in the output status. First we'll initialize some accounts: .. doctest:: >>> dm['bob-balance'] = 0.0 >>> dm['bob-credit'] = 0.0 >>> dm['sally-balance'] = 0.0 >>> dm['sally-credit'] = 100.0 >>> transaction.commit() Now, we'll define a validation function to validate an account: .. doctest:: >>> def validate_account(name): ... if dm[name+'-balance'] + dm[name+'-credit'] < 0: ... raise ValueError('Overdrawn', name) And a function to apply entries. If the function fails in some unexpected way, it rolls back all of its changes and prints the error: .. doctest:: >>> def apply_entries(entries): ... savepoint = transaction.savepoint() ... try: ... for name, amount in entries: ... entry_savepoint = transaction.savepoint() ... try: ... dm[name+'-balance'] += amount ... validate_account(name) ... except ValueError as error: ... entry_savepoint.rollback() ... print("%s %s" % ('Error', str(error))) ... else: ... print("%s %s" % ('Updated', name)) ... except Exception as error: ... savepoint.rollback() ... print("%s" % ('Unexpected exception')) Now let's try applying some entries: .. doctest:: >>> apply_entries([ ... ('bob', 10.0), ... ('sally', 10.0), ... ('bob', 20.0), ... ('sally', 10.0), ... ('bob', -100.0), ... ('sally', -100.0), ... ]) Updated bob Updated sally Updated bob Updated sally Error ('Overdrawn', 'bob') Updated sally >>> dm['bob-balance'] 30.0 >>> dm['sally-balance'] -80.0 If we provide entries that cause an unexpected error: .. doctest:: >>> apply_entries([ ... ('bob', 10.0), ... ('sally', 10.0), ... ('bob', '20.0'), ... ('sally', 10.0), ... ]) Updated bob Updated sally Unexpected exception Because the apply_entries used a savepoint for the entire function, it was able to rollback the partial changes without rolling back changes made in the previous call to ``apply_entries``: .. doctest:: >>> dm['bob-balance'] 30.0 >>> dm['sally-balance'] -80.0 If we now abort the outer transactions, the earlier changes will go away: .. doctest:: >>> transaction.abort() >>> dm['bob-balance'] 0.0 >>> dm['sally-balance'] 0.0 Savepoint invalidation ---------------------- A savepoint can be used any number of times: .. doctest:: >>> dm['bob-balance'] = 100.0 >>> dm['bob-balance'] 100.0 >>> savepoint = transaction.savepoint() >>> dm['bob-balance'] = 200.0 >>> dm['bob-balance'] 200.0 >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 >>> savepoint.rollback() # redundant, but should be harmless >>> dm['bob-balance'] 100.0 >>> dm['bob-balance'] = 300.0 >>> dm['bob-balance'] 300.0 >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 However, using a savepoint invalidates any savepoints that come after it: .. doctest:: >>> dm['bob-balance'] = 200.0 >>> dm['bob-balance'] 200.0 >>> savepoint1 = transaction.savepoint() >>> dm['bob-balance'] = 300.0 >>> dm['bob-balance'] 300.0 >>> savepoint2 = transaction.savepoint() >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 >>> savepoint2.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... InvalidSavepointRollbackError: invalidated by a later savepoint >>> savepoint1.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... InvalidSavepointRollbackError: invalidated by a later savepoint >>> transaction.abort() Databases without savepoint support ----------------------------------- Normally it's an error to use savepoints with databases that don't support savepoints: .. doctest:: >>> dm_no_sp = savepointsample.SampleDataManager() >>> dm_no_sp['name'] = 'bob' >>> transaction.commit() >>> dm_no_sp['name'] = 'sally' >>> transaction.savepoint() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'bob'}) >>> transaction.abort() However, a flag can be passed to the transaction savepoint method to indicate that databases without savepoint support should be tolerated until a savepoint is rolled back. This allows transactions to proceed if there are no reasons to roll back: .. doctest:: >>> dm_no_sp['name'] = 'sally' >>> savepoint = transaction.savepoint(1) >>> dm_no_sp['name'] = 'sue' >>> transaction.commit() >>> dm_no_sp['name'] 'sue' >>> dm_no_sp['name'] = 'sam' >>> savepoint = transaction.savepoint(1) >>> savepoint.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'sam'}) Failures -------- If a failure occurs when creating or rolling back a savepoint, the transaction state will be uncertain and the transaction will become uncommitable. From that point on, most transaction operations, including commit, will fail until the transaction is aborted. In the previous example, we got an error when we tried to rollback the savepoint. If we try to commit the transaction, the commit will fail: .. doctest:: >>> transaction.commit() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TransactionFailedError: An operation previously failed, with traceback: ... TypeError: ('Savepoints unsupported', {'name': 'sam'}) We have to abort it to make any progress: .. doctest:: >>> transaction.abort() Similarly, in our earlier example, where we tried to take a savepoint with a data manager that didn't support savepoints: .. doctest:: >>> dm_no_sp['name'] = 'sally' >>> dm['name'] = 'sally' >>> savepoint = transaction.savepoint() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'sue'}) >>> transaction.commit() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TransactionFailedError: An operation previously failed, with traceback: ... TypeError: ('Savepoints unsupported', {'name': 'sue'}) >>> transaction.abort() After clearing the transaction with an abort, we can get on with new transactions: .. doctest:: >>> dm_no_sp['name'] = 'sally' >>> dm['name'] = 'sally' >>> transaction.commit() >>> dm_no_sp['name'] 'sally' >>> dm['name'] 'sally' transaction-1.4.3/docs/index.rst0000664000175000017500000000742112073316126016550 0ustar tseavertseaver:mod:`transaction` Documentation ================================ Transaction objects manage resources for an individual activity. Compatibility issues -------------------- The implementation of Transaction objects involves two layers of backwards compatibility, because this version of transaction supports both ZODB 3 and ZODB 4. Zope is evolving towards the ZODB4 interfaces. Transaction has two methods for a resource manager to call to participate in a transaction -- register() and join(). join() takes a resource manager and adds it to the list of resources. register() is for backwards compatibility. It takes a persistent object and registers its _p_jar attribute. TODO: explain adapter Two-phase commit ---------------- A transaction commit involves an interaction between the transaction object and one or more resource managers. The transaction manager calls the following four methods on each resource manager; it calls tpc_begin() on each resource manager before calling commit() on any of them. 1. tpc_begin(txn) 2. commit(txn) 3. tpc_vote(txn) 4. tpc_finish(txn) Before-commit hook ------------------ Sometimes, applications want to execute some code when a transaction is committed. For example, one might want to delay object indexing until a transaction commits, rather than indexing every time an object is changed. Or someone might want to check invariants only after a set of operations. A pre-commit hook is available for such use cases: use addBeforeCommitHook(), passing it a callable and arguments. The callable will be called with its arguments at the start of the commit (but not for substransaction commits). After-commit hook ------------------ Sometimes, applications want to execute code after a transaction commit attempt succeeds or aborts. For example, one might want to launch non transactional code after a successful commit. Or still someone might want to launch asynchronous code after. A post-commit hook is available for such use cases: use addAfterCommitHook(), passing it a callable and arguments. The callable will be called with a Boolean value representing the status of the commit operation as first argument (true if successfull or false iff aborted) preceding its arguments at the start of the commit (but not for substransaction commits). Commit hooks are not called for transaction.abort(). Error handling -------------- When errors occur during two-phase commit, the transaction manager aborts all the resource managers. The specific methods it calls depend on whether the error occurs before or after the call to tpc_vote() on that transaction manager. If the resource manager has not voted, then the resource manager will have one or more uncommitted objects. There are two cases that lead to this state; either the transaction manager has not called commit() for any objects on this resource manager or the call that failed was a commit() for one of the objects of this resource manager. For each uncommitted object, including the object that failed in its commit(), call abort(). Once uncommitted objects are aborted, tpc_abort() or abort_sub() is called on each resource manager. Synchronization --------------- You can register sychronization objects (synchronizers) with the tranasction manager. The synchronizer must implement beforeCompletion() and afterCompletion() methods. The transaction manager calls beforeCompletion() when it starts a top-level two-phase commit. It calls afterCompletion() when a top-level transaction is committed or aborted. The methods are passed the current Transaction as their only argument. Contents: .. toctree:: :maxdepth: 2 convenience doom savepoint hooks datamanager resourcemanager api Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` transaction-1.4.3/docs/_build/0000775000175000017500000000000012312641352016137 5ustar tseavertseavertransaction-1.4.3/docs/_build/doctest/0000775000175000017500000000000012312641352017604 5ustar tseavertseavertransaction-1.4.3/docs/_build/doctest/output.txt0000664000175000017500000000214712312641213021705 0ustar tseavertseaverResults of doctest builder run on 2014-03-20 15:33:31 ===================================================== Document: convenience --------------------- 1 items passed all tests: 23 tests in default 23 tests in 1 items. 23 passed and 0 failed. Test passed. Document: doom -------------- 1 items passed all tests: 32 tests in default 32 tests in 1 items. 32 passed and 0 failed. Test passed. Document: savepoint ------------------- 1 items passed all tests: 76 tests in default 76 tests in 1 items. 76 passed and 0 failed. Test passed. Document: resourcemanager ------------------------- 1 items passed all tests: 122 tests in default 122 tests in 1 items. 122 passed and 0 failed. Test passed. Document: datamanager --------------------- 1 items passed all tests: 112 tests in default 112 tests in 1 items. 112 passed and 0 failed. Test passed. Document: hooks --------------- 1 items passed all tests: 136 tests in default 136 tests in 1 items. 136 passed and 0 failed. Test passed. Doctest summary =============== 501 tests 0 failures in tests 0 failures in setup code 0 failures in cleanup code transaction-1.4.3/docs/_build/doctrees/0000775000175000017500000000000012312641352017747 5ustar tseavertseavertransaction-1.4.3/docs/_build/doctrees/index.doctree0000664000175000017500000004265212312641212022431 0ustar tseavertseavercdocutils.nodes document q)q}q(Urefnamesq}qUsubstitution_defsq}qUautofootnote_refsq]q Utagnameq Udocumentq U rawsourceq UU nametypesq }q(Xcompatibility issuesqNXindices and tablesqNXtwo-phase commitqNXtransaction documentationqNXerror handlingqNXafter-commit hookqNXbefore-commit hookqNXsynchronizationqNuUreporterqNUsymbol_footnote_startqKUid_startqKh hUnameidsq}q(hUcompatibility-issuesqhUindices-and-tablesqhUtwo-phase-commitqhUtransaction-documentationqhUerror-handlingq hUafter-commit-hookq!hUbefore-commit-hookq"hUsynchronizationq#uUsymbol_footnote_refsq$]q%Uindirect_targetsq&]q'Ucurrent_sourceq(NUparse_messagesq)]q*U autofootnotesq+]q,Urefidsq-}q.U transformerq/NUsymbol_footnotesq0]q1U footnotesq2]q3U citation_refsq4}q5Usubstitution_namesq6}q7U citationsq8]q9U attributesq:}q;(Uidsq<]q=Udupnamesq>]q?Usourceq@cdocutils.nodes reprunicode qAX;/home/tseaver/projects/Zope/ZODB/transaction/docs/index.rstqBqC}qDbUbackrefsqE]qFUnamesqG]qHUclassesqI]qJuUidsqK}qL(h cdocutils.nodes section qM)qN}qO(h:}qP(hI]qQhE]qRh<]qSh ah>]qThG]qUhauh hUlineqVK]qahG]qbuh hhVK]qshG]qtuh hhVK>h U paragraphquhkhCh XWhen errors occur during two-phase commit, the transaction manager aborts all the resource managers. The specific methods it calls depend on whether the error occurs before or after the call to tpc_vote() on that transaction manager.qvhX]qwheXWhen errors occur during two-phase commit, the transaction manager aborts all the resource managers. The specific methods it calls depend on whether the error occurs before or after the call to tpc_vote() on that transaction manager.qxqy}qz(hihmh hvubahihNubhl)q{}q|(h:}q}(hI]q~hE]qh<]qh>]qhG]quh hhVKCh huhkhCh XIf the resource manager has not voted, then the resource manager will have one or more uncommitted objects. There are two cases that lead to this state; either the transaction manager has not called commit() for any objects on this resource manager or the call that failed was a commit() for one of the objects of this resource manager. For each uncommitted object, including the object that failed in its commit(), call abort().qhX]qheXIf the resource manager has not voted, then the resource manager will have one or more uncommitted objects. There are two cases that lead to this state; either the transaction manager has not called commit() for any objects on this resource manager or the call that failed was a commit() for one of the objects of this resource manager. For each uncommitted object, including the object that failed in its commit(), call abort().qq}q(hih{h hubahihNubhl)q}q(h:}q(hI]qhE]qh<]qh>]qhG]quh hhVKKh huhkhCh XdOnce uncommitted objects are aborted, tpc_abort() or abort_sub() is called on each resource manager.qhX]qheXdOnce uncommitted objects are aborted, tpc_abort() or abort_sub() is called on each resource manager.qq}q(hihh hubahihNubeh UhkhChihM)q}q(h:}q(hI]qhE]qh<]qhah>]qhG]qhauh hhVKh hWhX]q(hZ)q}q(h:}q(hI]qhE]qh<]qh>]qhG]quh hhVKh hchX]q(csphinx.addnodes pending_xref q)q}q(h:}q(UrefdocqXindexqUrefwarnqU py:moduleqNUpy:classqNh<]qh>]qU refexplicitqUreftypeqXmodqhE]qhG]qU refdomainqXpyqhI]qU reftargetqX transactionquhVKh U pending_xrefqhkhCh X:mod:`transaction`qhX]qcdocutils.nodes literal q)q}q(hihh UliteralqhX]qheX transactionqąq}q(hihh Uubah hh:}q(hI]q(UxrefqhXpy-modqehE]qh<]qh>]qhG]quubahihubheX Documentationqυq}q(hihh X Documentationqubeh X :mod:`transaction` DocumentationqhkhChihubhl)q}q(h:}q(hI]qhE]qh<]qh>]qhG]quh hhVKh huhkhCh X@Transaction objects manage resources for an individual activity.qhX]qheX@Transaction objects manage resources for an individual activity.qޅq}q(hihh hubahihubhM)q}q(h:}q(hI]qhE]qh<]qhah>]qhG]qhauh hhVKh hWhX]q(hZ)q}q(h:}q(hI]qhE]qh<]qh>]qhG]quh hhVKh hchX]qheXCompatibility issuesqq}q(hihh XCompatibility issuesqubah hhkhChihubhl)q}q(h:}q(hI]qhE]qh<]qh>]qhG]quh hhVK h huhkhCh XThe implementation of Transaction objects involves two layers of backwards compatibility, because this version of transaction supports both ZODB 3 and ZODB 4. Zope is evolving towards the ZODB4 interfaces.qhX]rheXThe implementation of Transaction objects involves two layers of backwards compatibility, because this version of transaction supports both ZODB 3 and ZODB 4. Zope is evolving towards the ZODB4 interfaces.rr}r(hihh hubahihubhl)r}r(h:}r(hI]rhE]rh<]r h>]r hG]r uh hhVKh huhkhCh X@Transaction has two methods for a resource manager to call to participate in a transaction -- register() and join(). join() takes a resource manager and adds it to the list of resources. register() is for backwards compatibility. It takes a persistent object and registers its _p_jar attribute. TODO: explain adapterr hX]r heX@Transaction has two methods for a resource manager to call to participate in a transaction -- register() and join(). join() takes a resource manager and adds it to the list of resources. register() is for backwards compatibility. It takes a persistent object and registers its _p_jar attribute. TODO: explain adapterrr}r(hijh j ubahihubeh UhkhChihubhM)r}r(h:}r(hI]rhE]rh<]rhah>]rhG]rhauh hhVKh hWhX]r(hZ)r}r(h:}r(hI]rhE]rh<]rh>]r hG]r!uh hhVKh hchX]r"heXTwo-phase commitr#r$}r%(hijh XTwo-phase commitr&ubah j&hkhChijubhl)r'}r((h:}r)(hI]r*hE]r+h<]r,h>]r-hG]r.uh hhVKh huhkhCh XA transaction commit involves an interaction between the transaction object and one or more resource managers. The transaction manager calls the following four methods on each resource manager; it calls tpc_begin() on each resource manager before calling commit() on any of them.r/hX]r0heXA transaction commit involves an interaction between the transaction object and one or more resource managers. The transaction manager calls the following four methods on each resource manager; it calls tpc_begin() on each resource manager before calling commit() on any of them.r1r2}r3(hij'h j/ubahijubcdocutils.nodes block_quote r4)r5}r6(h:}r7(hI]r8hE]r9h<]r:h>]r;hG]r<uh hhVNh U block_quoter=hX]r>cdocutils.nodes enumerated_list r?)r@}rA(hij5h Uenumerated_listrBhX]rC(cdocutils.nodes list_item rD)rE}rF(hij@h U list_itemrGhX]rHhl)rI}rJ(h:}rK(hI]rLhE]rMh<]rNh>]rOhG]rPuhVKh huhkhCh Xtpc_begin(txn)rQhX]rRheXtpc_begin(txn)rSrT}rU(hijIh jQubahijEubah jQh:}rV(hI]rWhE]rXh<]rYh>]rZhG]r[uubjD)r\}r](hij@h jGhX]r^hl)r_}r`(h:}ra(hI]rbhE]rch<]rdh>]rehG]rfuhVKh huhkhCh X commit(txn)rghX]rhheX commit(txn)rirj}rk(hij_h jgubahij\ubah jgh:}rl(hI]rmhE]rnh<]roh>]rphG]rquubjD)rr}rs(hij@h jGhX]rthl)ru}rv(h:}rw(hI]rxhE]ryh<]rzh>]r{hG]r|uhVKh huhkhCh X tpc_vote(txn)r}hX]r~heX tpc_vote(txn)rr}r(hijuh j}ubahijrubah j}h:}r(hI]rhE]rh<]rh>]rhG]ruubjD)r}r(hij@h jGhX]rhl)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruhVK h huhkhCh Xtpc_finish(txn)rhX]rheXtpc_finish(txn)rr}r(hijh jubahijubah Xtpc_finish(txn) rh:}r(hI]rhE]rh<]rh>]rhG]ruubeh Uh:}r(UenumtyperUarabicrUprefixrUh<]rh>]rUsuffixrU.hE]rhG]rhI]ruubah UhkNhijubeh UhkhChihubhM)r}r(h:}r(hI]rhE]rh<]rh"ah>]rhG]rhauh hhVK#h hWhX]r(hZ)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruh hhVK#h hchX]rheXBefore-commit hookrr}r(hijh XBefore-commit hookrubah jhkhChijubhl)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruh hhVK%h huhkhCh X Sometimes, applications want to execute some code when a transaction is committed. For example, one might want to delay object indexing until a transaction commits, rather than indexing every time an object is changed. Or someone might want to check invariants only after a set of operations. A pre-commit hook is available for such use cases: use addBeforeCommitHook(), passing it a callable and arguments. The callable will be called with its arguments at the start of the commit (but not for substransaction commits).rhX]rheX Sometimes, applications want to execute some code when a transaction is committed. For example, one might want to delay object indexing until a transaction commits, rather than indexing every time an object is changed. Or someone might want to check invariants only after a set of operations. A pre-commit hook is available for such use cases: use addBeforeCommitHook(), passing it a callable and arguments. The callable will be called with its arguments at the start of the commit (but not for substransaction commits).rr}r(hijh jubahijubeh UhkhChihubhM)r}r(h:}r(hI]rhE]rh<]rh!ah>]rhG]rhauh hhVK.h hWhX]r(hZ)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruh hhVK.h hchX]rheXAfter-commit hookrr}r(hijh XAfter-commit hookrubah jhkhChijubhl)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruh hhVK0h huhkhCh XSometimes, applications want to execute code after a transaction commit attempt succeeds or aborts. For example, one might want to launch non transactional code after a successful commit. Or still someone might want to launch asynchronous code after. A post-commit hook is available for such use cases: use addAfterCommitHook(), passing it a callable and arguments. The callable will be called with a Boolean value representing the status of the commit operation as first argument (true if successfull or false iff aborted) preceding its arguments at the start of the commit (but not for substransaction commits). Commit hooks are not called for transaction.abort().rhX]rheXSometimes, applications want to execute code after a transaction commit attempt succeeds or aborts. For example, one might want to launch non transactional code after a successful commit. Or still someone might want to launch asynchronous code after. A post-commit hook is available for such use cases: use addAfterCommitHook(), passing it a callable and arguments. The callable will be called with a Boolean value representing the status of the commit operation as first argument (true if successfull or false iff aborted) preceding its arguments at the start of the commit (but not for substransaction commits). Commit hooks are not called for transaction.abort().rr}r(hijh jubahijubeh UhkhChihubhNhM)r}r(h:}r(hI]rhE]rh<]rh#ah>]rhG]rhauh hhVKOh hWhX]r(hZ)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruh hhVKOh hchX]rheXSynchronizationrr}r(hijh XSynchronizationrubah jhkhChijubhl)r}r(h:}r(hI]rhE]r h<]r h>]r hG]r uh hhVKQh huhkhCh XYou can register sychronization objects (synchronizers) with the tranasction manager. The synchronizer must implement beforeCompletion() and afterCompletion() methods. The transaction manager calls beforeCompletion() when it starts a top-level two-phase commit. It calls afterCompletion() when a top-level transaction is committed or aborted. The methods are passed the current Transaction as their only argument.r hX]rheXYou can register sychronization objects (synchronizers) with the tranasction manager. The synchronizer must implement beforeCompletion() and afterCompletion() methods. The transaction manager calls beforeCompletion() when it starts a top-level two-phase commit. It calls afterCompletion() when a top-level transaction is committed or aborted. The methods are passed the current Transaction as their only argument.rr}r(hijh j ubahijubhl)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruh hhVKYh huhkhCh X Contents:rhX]rheX Contents:rr}r(hijh jubahijubcdocutils.nodes compound r)r }r!(h:}r"(hI]r#Utoctree-wrapperr$ahE]r%h<]r&h>]r'hG]r(uh hhVNh Ucompoundr)hX]r*csphinx.addnodes toctree r+)r,}r-(h:}r.(U includehiddenr/Uhiddenr0U titlesonlyr1h<]r2h>]r3Unumberedr4KUmaxdepthr5KhE]r6Uentriesr7]r8(NX conveniencer9r:NXdoomr;r<NX savepointr=r>NXhooksr?r@NX datamanagerrArBNXresourcemanagerrCrDNXapirErFeUglobrGhG]rHhI]rIhihU includefilesrJ]rK(j9j;j=j?jAjCjEeuhVK[h UtoctreerLhkhCh UhX]rMhij ubah UhkhChijubeh UhkhChihubeh UhkhChihububh#jhhM)rN}rO(h:}rP(hI]rQhE]rRh<]rShah>]rThG]rUhauh hhVKhh hWhX]rV(hZ)rW}rX(h:}rY(hI]rZhE]r[h<]r\h>]r]hG]r^uh hhVKhh hchX]r_heXIndices and tablesr`ra}rb(hijWh XIndices and tablesrcubah jchkhChijNubcdocutils.nodes bullet_list rd)re}rf(h:}rg(UbulletrhX*h<]rih>]rjhE]rkhG]rlhI]rmuh hhVKjh U bullet_listrnhX]ro(jD)rp}rq(h:}rr(hI]rshE]rth<]ruh>]rvhG]rwuh hhVNh jGhX]rxhl)ry}rz(h:}r{(hI]r|hE]r}h<]r~h>]rhG]ruhVKjh huhkhCh X:ref:`genindex`rhX]rh)r}r(h:}r(hhhh<]rh>]rU refexplicitrUreftyperXrefrhE]rhG]rU refdomainrXstdrhI]rhXgenindexruhVKjh hhkhCh jhX]rcdocutils.nodes emphasis r)r}r(hijh UemphasisrhX]rheXgenindexrr}r(hijh Uubah jh:}r(hI]r(hjXstd-refrehE]rh<]rh>]rhG]ruubahijyubahijpubah jhkhChijeubjD)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruh hhVNh jGhX]rhl)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruhVKkh huhkhCh X:ref:`modindex`rhX]rh)r}r(h:}r(hhhh<]rh>]rU refexplicitrUreftyperXrefrhE]rhG]rU refdomainrXstdrhI]rhXmodindexruhVKkh hhkhCh jhX]rj)r}r(hijh jhX]rheXmodindexrr}r(hijh Uubah jh:}r(hI]r(hjXstd-refrehE]rh<]rh>]rhG]ruubahijubahijubah jhkhChijeubjD)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruh hhVNh jGhX]rhl)r}r(h:}r(hI]rhE]rh<]rh>]rhG]ruhVKlh huhkhCh X :ref:`search`rhX]rh)r}r(h:}r(hhhh<]rh>]rU refexplicitrUreftyperXrefrhE]rhG]rU refdomainrXstdrhI]rhXsearchruhVKlh hhkhCh jhX]rj)r}r(hijh jhX]rheXsearchrr}r(hijh Uubah jh:}r(hI]r(hjXstd-refrehE]rh<]rh>]rhG]ruubahijubahijubah X:ref:`search` rhkhChijeubeh UhkhChijNubeh UhkhChihubh"jhhhhh!jhjuU decorationrNhX]r(hjNeU footnote_refsr}rU current_linerNUsettingsr(cdocutils.frontend Values ror}r(U sectnum_xformr KU halt_levelr KU datestampr NU tab_widthr KU toc_backlinksr UentryrUembed_stylesheetrUgettext_compactrU report_levelrKUstrip_commentsrNU source_linkrNUenvrNU smart_quotesrU_disable_configrNUsectsubtitle_xformrUinput_encoding_error_handlerrUstrictrU rfc_base_urlrUhttp://tools.ietf.org/html/rUfile_insertion_enabledrUdebugrNUwarning_streamrNU strip_classesrNUoutput_encodingr Uutf-8r!U docinfo_xformr"KU source_urlr#NUdoctitle_xformr$Udump_internalsr%NUfootnote_backlinksr&KU id_prefixr'UUpep_referencesr(NhcNUauto_id_prefixr)Uidr*Ustrip_elements_with_classesr+NUoutput_encoding_error_handlerr,jU _config_filesr-]r.U pep_base_urlr/Uhttp://www.python.org/dev/peps/r0U _destinationr1NUerror_encodingr2UUTF-8r3U tracebackr4Urfc_referencesr5NUexpose_internalsr6NUtrim_footnote_reference_spacer7Urecord_dependenciesr8NU_sourcer9U;/home/tseaver/projects/Zope/ZODB/transaction/docs/index.rstr:U raw_enabledr;KUconfigr<NU generatorr=NUerror_encoding_error_handlerr>Ubackslashreplacer?U language_coder@UenrAUinput_encodingrBU utf-8-sigrCUstrict_visitorrDNUsyntax_highlightrEUlongrFUexit_status_levelrGKU dump_settingsrHNUcloak_email_addressesrIUdump_transformsrJNUdump_pseudo_xmlrKNUpep_file_url_templaterLUpep-%04drMubUtransform_messagesrN]rOUautofootnote_startrPKub.transaction-1.4.3/docs/_build/doctrees/doom.doctree0000664000175000017500000003752112312641212022257 0ustar tseavertseavercdocutils.nodes document q)q}q(Urefnamesq}qUsubstitution_defsq}qUautofootnote_refsq]q Utagnameq Udocumentq U rawsourceq UU nametypesq }qXdooming transactionsqNsUreporterqNUsymbol_footnote_startqKUid_startqKh hUnameidsq}qhUdooming-transactionsqsUsymbol_footnote_refsq]qUindirect_targetsq]qUcurrent_sourceqNUparse_messagesq]qU autofootnotesq]qUrefidsq}q U transformerq!NUsymbol_footnotesq"]q#U footnotesq$]q%U citation_refsq&}q'Usubstitution_namesq(}q)U citationsq*]q+U attributesq,}q-(Uidsq.]q/Udupnamesq0]q1Usourceq2cdocutils.nodes reprunicode q3X:/home/tseaver/projects/Zope/ZODB/transaction/docs/doom.rstq4q5}q6bUbackrefsq7]q8Unamesq9]q:Uclassesq;]qhcdocutils.nodes section q?)q@}qA(h,}qB(h;]qCh7]qDh.]qEhah0]qFh9]qGhauh hUlineqHKh UsectionqIUchildrenqJ]qK(cdocutils.nodes title qL)qM}qN(h,}qO(h;]qPh7]qQh.]qRh0]qSh9]qTuh hhHKh UtitleqUhJ]qVcdocutils.nodes Text qWXDooming TransactionsqXqY}qZ(Uparentq[hMh XDooming Transactionsq\ubah h\Usourceq]h5h[h@ubcdocutils.nodes paragraph q^)q_}q`(h,}qa(h;]qbh7]qch.]qdh0]qeh9]qfuh hhHKh U paragraphqgh]h5h XA doomed transaction behaves exactly the same way as an active transaction but raises an error on any attempt to commit it, thus forcing an abort.qhhJ]qihWXA doomed transaction behaves exactly the same way as an active transaction but raises an error on any attempt to commit it, thus forcing an abort.qjqk}ql(h[h_h hhubah[h@ubh^)qm}qn(h,}qo(h;]qph7]qqh.]qrh0]qsh9]qtuh hhHKh hgh]h5h XDoom is useful in places where abort is unsafe and an exception cannot be raised. This occurs when the programmer wants the code following the doom to run but not commit. It is unsafe to abort in these circumstances as a following get() may implicitly open a new transaction.quhJ]qvhWXDoom is useful in places where abort is unsafe and an exception cannot be raised. This occurs when the programmer wants the code following the doom to run but not commit. It is unsafe to abort in these circumstances as a following get() may implicitly open a new transaction.qwqx}qy(h[hmh huubah[h@ubh^)qz}q{(h,}q|(h;]q}h7]q~h.]qh0]qh9]quh hhHK h hgh]h5h XTAny attempt to commit a doomed transaction will raise a DoomedTransaction exception.qhJ]qhWXTAny attempt to commit a doomed transaction will raise a DoomedTransaction exception.qq}q(h[hzh hubah[h@ubh^)q}q(h,}q(h;]qh7]qh.]qh0]qh9]quh hhHKh hgh]h5h X!An example of such a use case can be found in zope/app/form/browser/editview.py. Here a form validation failure must doom the transaction as committing the transaction may have side-effects. However, the form code must continue to calculate a form containing the error messages to return.qhJ]qhWX!An example of such a use case can be found in zope/app/form/browser/editview.py. Here a form validation failure must doom the transaction as committing the transaction may have side-effects. However, the form code must continue to calculate a form containing the error messages to return.qq}q(h[hh hubah[h@ubh^)q}q(h,}q(h;]qh7]qh.]qh0]qh9]quh hhHKh hgh]h5h XFor Zope in general, code running within a request should always doom transactions rather than aborting them. It is the responsibilty of the publication to either abort() or commit() the transaction. Application code can use savepoints and doom() safely.qhJ]qhWXFor Zope in general, code running within a request should always doom transactions rather than aborting them. It is the responsibilty of the publication to either abort() or commit() the transaction. Application code can use savepoints and doom() safely.qq}q(h[hh hubah[h@ubh^)q}q(h,}q(h;]qh7]qh.]qh0]qh9]quh hhHKh hgh]h5h X@To see how it works we first need to create a stub data manager:qhJ]qhWX@To see how it works we first need to create a stub data manager:qq}q(h[hh hubah[h@ubcdocutils.nodes literal_block q)q}q(h,}q(U testnodetypeqXdoctestqUgroupsq]qUdefaultqah.]qh0]qh7]qU xml:spaceqUpreserveqUoptionsq}qh9]qh;]quh hhHKh U literal_blockqh]h5h XT>>> from transaction.interfaces import IDataManager >>> from zope.interface import implementer >>> @implementer(IDataManager) ... class DataManager: ... def __init__(self): ... self.attr_counter = {} ... def __getattr__(self, name): ... def f(transaction): ... self.attr_counter[name] = self.attr_counter.get(name, 0) + 1 ... return f ... def total(self): ... count = 0 ... for access_count in self.attr_counter.values(): ... count += access_count ... return count ... def sortKey(self): ... return 1qhJ]qhWXT>>> from transaction.interfaces import IDataManager >>> from zope.interface import implementer >>> @implementer(IDataManager) ... class DataManager: ... def __init__(self): ... self.attr_counter = {} ... def __getattr__(self, name): ... def f(transaction): ... self.attr_counter[name] = self.attr_counter.get(name, 0) + 1 ... return f ... def total(self): ... count = 0 ... for access_count in self.attr_counter.values(): ... count += access_count ... return count ... def sortKey(self): ... return 1qÅq}q(h[hh Uubah[h@ubh^)q}q(h,}q(h;]qh7]qh.]qh0]qh9]quh hhHK0h hgh]h5h XStart a new transaction:qhJ]qhWXStart a new transaction:qЅq}q(h[hh hubah[h@ubh)q}q(h,}q(U testnodetypeqXdoctestqUgroupsq]qhah.]qh0]qh7]qhhh}qh9]qh;]quh hhHK2h hh]h5h X\>>> import transaction >>> txn = transaction.begin() >>> dm = DataManager() >>> txn.join(dm)qhJ]qhWX\>>> import transaction >>> txn = transaction.begin() >>> dm = DataManager() >>> txn.join(dm)q⅁q}q(h[hh Uubah[h@ubh^)q}q(h,}q(h;]qh7]qh.]qh0]qh9]quh hhHK9h hgh]h5h XrWe can ask a transaction if it is doomed to avoid expensive operations. An example of a use case is an object-relational mapper where a pre-commit hook sends all outstanding SQL to a relational database for objects changed during the transaction. This expensive operation is not necessary if the transaction has been doomed. A non-doomed transaction should return False:qhJ]qhWXrWe can ask a transaction if it is doomed to avoid expensive operations. An example of a use case is an object-relational mapper where a pre-commit hook sends all outstanding SQL to a relational database for objects changed during the transaction. This expensive operation is not necessary if the transaction has been doomed. A non-doomed transaction should return False:qq}q(h[hh hubah[h@ubh)q}q(h,}q(U testnodetypeqXdoctestqUgroupsq]qhah.]qh0]qh7]qhhh}qh9]qh;]quh hhHK?h hh]h5h X>>> txn.isDoomed() FalseqhJ]rhWX>>> txn.isDoomed() Falserr}r(h[hh Uubah[h@ubh^)r}r(h,}r(h;]rh7]rh.]r h0]r h9]r uh hhHKDh hgh]h5h X3We can doom a transaction by calling .doom() on it:r hJ]r hWX3We can doom a transaction by calling .doom() on it:rr}r(h[jh j ubah[h@ubh)r}r(h,}r(U testnodetyperXdoctestrUgroupsr]rhah.]rh0]rh7]rhhh}rh9]rh;]ruh hhHKFh hh]h5h X&>>> txn.doom() >>> txn.isDoomed() TruerhJ]rhWX&>>> txn.doom() >>> txn.isDoomed() Truer r!}r"(h[jh Uubah[h@ubh^)r#}r$(h,}r%(h;]r&h7]r'h.]r(h0]r)h9]r*uh hhHKLh hgh]h5h X We can doom it again if we like:r+hJ]r,hWX We can doom it again if we like:r-r.}r/(h[j#h j+ubah[h@ubh)r0}r1(h,}r2(U testnodetyper3Xdoctestr4Ugroupsr5]r6hah.]r7h0]r8h7]r9hhh}r:h9]r;h;]r<uh hhHKNh hh]h5h X>>> txn.doom()r=hJ]r>hWX>>> txn.doom()r?r@}rA(h[j0h Uubah[h@ubh^)rB}rC(h,}rD(h;]rEh7]rFh.]rGh0]rHh9]rIuh hhHKRh hgh]h5h X,The data manager is unchanged at this point:rJhJ]rKhWX,The data manager is unchanged at this point:rLrM}rN(h[jBh jJubah[h@ubh)rO}rP(h,}rQ(U testnodetyperRXdoctestrSUgroupsrT]rUhah.]rVh0]rWh7]rXhhh}rYh9]rZh;]r[uh hhHKTh hh]h5h X>>> dm.total() 0r\hJ]r]hWX>>> dm.total() 0r^r_}r`(h[jOh Uubah[h@ubh^)ra}rb(h,}rc(h;]rdh7]reh.]rfh0]rgh9]rhuh hhHKYh hgh]h5h XYAttempting to commit a doomed transaction any number of times raises a DoomedTransaction:rihJ]rjhWXYAttempting to commit a doomed transaction any number of times raises a DoomedTransaction:rkrl}rm(h[jah jiubah[h@ubh)rn}ro(h,}rp(UtestrqX>>> txn.commit() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): DoomedTransaction: transaction doomed, cannot commit >>> txn.commit() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): DoomedTransaction: transaction doomed, cannot commitrrU testnodetypersXdoctestrtUgroupsru]rvhah.]rwh0]rxh7]ryhhh}rzh9]r{h;]r|uh hhHK\h hh]h5h X>>> txn.commit() Traceback (most recent call last): DoomedTransaction: transaction doomed, cannot commit >>> txn.commit() Traceback (most recent call last): DoomedTransaction: transaction doomed, cannot commitr}hJ]r~hWX>>> txn.commit() Traceback (most recent call last): DoomedTransaction: transaction doomed, cannot commit >>> txn.commit() Traceback (most recent call last): DoomedTransaction: transaction doomed, cannot commitrr}r(h[jnh Uubah[h@ubh^)r}r(h,}r(h;]rh7]rh.]rh0]rh9]ruh hhHKeh hgh]h5h X,But still leaves the data manager unchanged:rhJ]rhWX,But still leaves the data manager unchanged:rr}r(h[jh jubah[h@ubh)r}r(h,}r(U testnodetyperXdoctestrUgroupsr]rhah.]rh0]rh7]rhhh}rh9]rh;]ruh hhHKgh hh]h5h X>>> dm.total() 0rhJ]rhWX>>> dm.total() 0rr}r(h[jh Uubah[h@ubh^)r}r(h,}r(h;]rh7]rh.]rh0]rh9]ruh hhHKlh hgh]h5h X*But the doomed transaction can be aborted:rhJ]rhWX*But the doomed transaction can be aborted:rr}r(h[jh jubah[h@ubh)r}r(h,}r(U testnodetyperXdoctestrUgroupsr]rhah.]rh0]rh7]rhhh}rh9]rh;]ruh hhHKnh hh]h5h X>>> txn.abort()rhJ]rhWX>>> txn.abort()rr}r(h[jh Uubah[h@ubh^)r}r(h,}r(h;]rh7]rh.]rh0]rh9]ruh hhHKrh hgh]h5h XWhich aborts the data manager:rhJ]rhWXWhich aborts the data manager:rr}r(h[jh jubah[h@ubh)r}r(h,}r(U testnodetyperXdoctestrUgroupsr]rhah.]rh0]rh7]rhhh}rh9]rh;]ruh hhHKth hh]h5h X/>>> dm.total() 1 >>> dm.attr_counter['abort'] 1rhJ]rhWX/>>> dm.total() 1 >>> dm.attr_counter['abort'] 1rr}r(h[jh Uubah[h@ubh^)r}r(h,}r(h;]rh7]rh.]rh0]rh9]ruh hhHK{h hgh]h5h XDooming the current transaction can also be done directly from the transaction module. We can also begin a new transaction directly after dooming the old one:rhJ]rhWXDooming the current transaction can also be done directly from the transaction module. We can also begin a new transaction directly after dooming the old one:rr}r(h[jh jubah[h@ubh)r}r(h,}r(U testnodetyperXdoctestrUgroupsr]rhah.]rh0]rh7]rhhh}rh9]rh;]ruh hhHK~h hh]h5h X>>> txn = transaction.begin() >>> transaction.isDoomed() False >>> transaction.doom() >>> transaction.isDoomed() True >>> txn = transaction.begin()rhJ]rhWX>>> txn = transaction.begin() >>> transaction.isDoomed() False >>> transaction.doom() >>> transaction.isDoomed() True >>> txn = transaction.begin()rr}r(h[jh Uubah[h@ubh^)r}r(h,}r(h;]rh7]rh.]rh0]rh9]ruh hhHKh hgh]h5h XAfter committing a transaction we get an assertion error if we try to doom the transaction. This could be made more specific, but trying to doom a transaction after it's been committed is probably a programming error:rhJ]rhWXAfter committing a transaction we get an assertion error if we try to doom the transaction. This could be made more specific, but trying to doom a transaction after it's been committed is probably a programming error:rr }r (h[jh jubah[h@ubh)r }r (h,}r (U testnodetyperXdoctestrUgroupsr]rhah.]rh0]rh7]rhhh}rh9]rh;]ruh hhHKh hh]h5h X>>> txn = transaction.begin() >>> txn.commit() >>> txn.doom() Traceback (most recent call last): ... ValueError: non-doomablerhJ]rhWX>>> txn = transaction.begin() >>> txn.commit() >>> txn.doom() Traceback (most recent call last): ... ValueError: non-doomablerr}r(h[j h Uubah[h@ubh^)r}r(h,}r(h;]r h7]r!h.]r"h0]r#h9]r$uh hhHKh hgh]h5h XcA doomed transaction should act the same as an active transaction, so we should be able to join it:r%hJ]r&hWXcA doomed transaction should act the same as an active transaction, so we should be able to join it:r'r(}r)(h[jh j%ubah[h@ubh)r*}r+(h,}r,(U testnodetyper-Xdoctestr.Ugroupsr/]r0hah.]r1h0]r2h7]r3hhh}r4h9]r5h;]r6uh hhHKh hh]h5h XV>>> txn = transaction.begin() >>> txn.doom() >>> dm2 = DataManager() >>> txn.join(dm2)r7hJ]r8hWXV>>> txn = transaction.begin() >>> txn.doom() >>> dm2 = DataManager() >>> txn.join(dm2)r9r:}r;(h[j*h Uubah[h@ubh^)r<}r=(h,}r>(h;]r?h7]r@h.]rAh0]rBh9]rCuh hhHKh hgh]h5h X Clean up:rDhJ]rEhWX Clean up:rFrG}rH(h[j<h jDubah[h@ubh)rI}rJ(h,}rK(U testnodetyperLXdoctestrMUgroupsrN]rOhah.]rPh0]rQh7]rRhhh}rSh9]rTh;]rUuh hhHKh hh]h5h X->>> txn = transaction.begin() >>> txn.abort()rVhJ]rWhWX->>> txn = transaction.begin() >>> txn.abort()rXrY}rZ(h[jIh Uubah[h@ubeh Uh]h5h[hubsU decorationr[NhJ]r\h@aU footnote_refsr]}r^U current_liner_NUsettingsr`(cdocutils.frontend Values raorb}rc(U sectnum_xformrdKU halt_levelreKU datestamprfNU tab_widthrgKU toc_backlinksrhUentryriUembed_stylesheetrjUgettext_compactrkU report_levelrlKUstrip_commentsrmNU source_linkrnNUenvroNU smart_quotesrpU_disable_configrqNUsectsubtitle_xformrrUinput_encoding_error_handlerrsUstrictrtU rfc_base_urlruUhttp://tools.ietf.org/html/rvUfile_insertion_enabledrwUdebugrxNUwarning_streamryNU strip_classesrzNUoutput_encodingr{Uutf-8r|U docinfo_xformr}KU source_urlr~NUdoctitle_xformrUdump_internalsrNUfootnote_backlinksrKU id_prefixrUUpep_referencesrNhUNUauto_id_prefixrUidrUstrip_elements_with_classesrNUoutput_encoding_error_handlerrjtU _config_filesr]rU pep_base_urlrUhttp://www.python.org/dev/peps/rU _destinationrNUerror_encodingrUUTF-8rU tracebackrUrfc_referencesrNUexpose_internalsrNUtrim_footnote_reference_spacerUrecord_dependenciesrNU_sourcerU:/home/tseaver/projects/Zope/ZODB/transaction/docs/doom.rstrU raw_enabledrKUconfigrNU generatorrNUerror_encoding_error_handlerrUbackslashreplacerU language_coderUenrUinput_encodingrU utf-8-sigrUstrict_visitorrNUsyntax_highlightrUlongrUexit_status_levelrKU dump_settingsrNUcloak_email_addressesrUdump_transformsrNUdump_pseudo_xmlrNUpep_file_url_templaterUpep-%04drubUtransform_messagesr]rUautofootnote_startrKub.transaction-1.4.3/docs/_build/doctrees/resourcemanager.doctree0000664000175000017500000010177412312641212024505 0ustar tseavertseavercdocutils.nodes document q)q}q(Urefnamesq}qUsubstitution_defsq}qUautofootnote_refsq]q Utagnameq Udocumentq U rawsourceq UU nametypesq }q(Xthe tpc_abort() methodqNXthe tpc_vote() methodqNXthe tpc_begin() methodqNXthe savepoint() methodqNXwriting a resource managerqNXthe tpc_finish() methodqNXsimple resource managerqNuUreporterqNUsymbol_footnote_startqKUid_startqKh hUnameidsq}q(hUthe-tpc-abort-methodqhUthe-tpc-vote-methodqhUthe-tpc-begin-methodqhUthe-savepoint-methodqhUwriting-a-resource-managerqhUthe-tpc-finish-methodq hUsimple-resource-managerq!uUsymbol_footnote_refsq"]q#Uindirect_targetsq$]q%Ucurrent_sourceq&NUparse_messagesq']q(U autofootnotesq)]q*Urefidsq+}q,U transformerq-NUsymbol_footnotesq.]q/U footnotesq0]q1U citation_refsq2}q3Usubstitution_namesq4}q5U citationsq6]q7U attributesq8}q9(Uidsq:]q;Udupnamesq<]q=Usourceq>cdocutils.nodes reprunicode q?XE/home/tseaver/projects/Zope/ZODB/transaction/docs/resourcemanager.rstq@qA}qBbUbackrefsqC]qDUnamesqE]qFUclassesqG]qHuUidsqI}qJ(hcdocutils.nodes section qK)qL}qM(h8}qN(hG]qOhC]qPh:]qQhah<]qRhE]qShauh hUlineqTKXh UsectionqUUchildrenqV]qW(cdocutils.nodes title qX)qY}qZ(h8}q[(hG]q\hC]q]h:]q^h<]q_hE]q`uh hhTKXh UtitleqahV]qb(cdocutils.nodes Text qcXThe qdqe}qf(UparentqghYh XThe qhubcsphinx.addnodes pending_xref qi)qj}qk(h8}ql(UrefdocqmXresourcemanagerqnUrefwarnqoU py:moduleqpNUpy:classqqNh:]qrh<]qsU refexplicitqtUreftypequXmethqvhC]qwhE]qxU refdomainqyXpyqzhG]q{U reftargetq|X tpc_beginq}uhTKWh U pending_xrefq~UsourceqhAh X:meth:`tpc_begin`qhV]qcdocutils.nodes literal q)q}q(hghjh UliteralqhV]qhcX tpc_begin()qq}q(hghh Uubah hh8}q(hG]q(UxrefqhzXpy-methqehC]qh:]qh<]qhE]quubahghYubhcX Methodqq}q(hghYh X Methodqubeh XThe :meth:`tpc_begin` MethodqhhAhghLubcdocutils.nodes paragraph q)q}q(h8}q(hG]qhC]qh:]qh<]qhE]quh hhTKZh U paragraphqhhAh XJCalled by the transaction manager to ask the RM to prepare to commit data.qhV]qhcXJCalled by the transaction manager to ask the RM to prepare to commit data.qq}q(hghh hubahghLubcdocutils.nodes literal_block q)q}q(h8}q(U testnodetypeqXdoctestqUgroupsq]qUdefaultqah:]qh<]qhC]qU xml:spaceqUpreserveqUoptionsq}qhE]qhG]quh hhTK\h U literal_blockqhhAh X>>> rm = ResourceManager() >>> rm.inc() >>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> rm.state 1 >>> rm.inc() >>> t2 = '2' >>> rm.tpc_begin(t2) >>> rm.tpc_vote(t2) >>> rm.tpc_abort(t2) >>> rm.state 1qhV]qhcX>>> rm = ResourceManager() >>> rm.inc() >>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> rm.state 1 >>> rm.inc() >>> t2 = '2' >>> rm.tpc_begin(t2) >>> rm.tpc_vote(t2) >>> rm.tpc_abort(t2) >>> rm.state 1qq}q(hghh UubahghLubh)q}q(h8}q(hG]qhC]qh:]qh<]qhE]quh hhTKnh hhhAh XTIt is an error to call tpc_begin more than once without completing two-phase commit:qhV]qhcXTIt is an error to call tpc_begin more than once without completing two-phase commit:qȅq}q(hghh hubahghLubh)q}q(h8}q(U testnodetypeqXdoctestqUgroupsq]qhah:]qh<]qhC]qhhh}qhE]qhG]quh hhTKqh hhhAh X>>> rm.tpc_begin(t1) >>> rm.tpc_begin(t1) Traceback (most recent call last): ... ValueError: txn in state 'tpc_begin' but expected one of (None,) >>> rm.tpc_abort(t1)qhV]qhcX>>> rm.tpc_begin(t1) >>> rm.tpc_begin(t1) Traceback (most recent call last): ... ValueError: txn in state 'tpc_begin' but expected one of (None,) >>> rm.tpc_abort(t1)qڅq}q(hghh UubahghLubh)q}q(h8}q(hG]qhC]qh:]qh<]qhE]quh hhTK{h hhhAh X@If there was a preceeding savepoint, the transaction must match:qhV]qhcX@If there was a preceeding savepoint, the transaction must match:q煁q}q(hghh hubahghLubh)q}q(h8}q(U testnodetypeqXdoctestqUgroupsq]qhah:]qh<]qhC]qhhh}qhE]qhG]quh hhTK}h hhhAh X>>> rollback = rm.savepoint(t1) >>> rm.tpc_begin(t2) Traceback (most recent call last): ,,, TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_begin(t1)qhV]qhcX>>> rollback = rm.savepoint(t1) >>> rm.tpc_begin(t2) Traceback (most recent call last): ,,, TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_begin(t1)qq}q(hghh UubahghLubeh UhhAhghK)q}q(h8}q(hG]qhC]rh:]rhah<]rhE]rhauh hhTKh hUhV]r(hX)r}r(h8}r(hG]rhC]r h:]r h<]r hE]r uh hhTKh hahV]r hcXWriting a Resource Managerrr}r(hgjh XWriting a Resource Managerrubah jhhAhghubhK)r}r(h8}r(hG]rhC]rh:]rh!ah<]rhE]rhauh hhTKh hUhV]r(hX)r}r(h8}r(hG]rhC]rh:]r h<]r!hE]r"uh hhTKh hahV]r#hcXSimple Resource Managerr$r%}r&(hgjh XSimple Resource Managerr'ubah j'hhAhgjubh)r(}r)(h8}r*(U testnodetyper+Xdoctestr,Ugroupsr-]r.hah:]r/h<]r0hC]r1hhh}r2hE]r3hG]r4uh hhTKh hhhAh X:>>> from transaction.tests.examples import ResourceManagerr5hV]r6hcX:>>> from transaction.tests.examples import ResourceManagerr7r8}r9(hgj(h Uubahgjubh)r:}r;(h8}r<(hG]r=hC]r>h:]r?h<]r@hE]rAuh hhTK h hhhAh XThis :class:`transaction.tests.examples.ResourceManager` class provides a trivial resource-manager implementation and doc strings to illustrate the protocol and to provide a tool for writing tests.rBhV]rC(hcXThis rDrE}rF(hgj:h XThis rGubhi)rH}rI(h8}rJ(hmhnhohpNhqNh:]rKh<]rLU refexplicitrMUreftyperNXclassrOhC]rPhE]rQU refdomainrRXpyrShG]rTh|X*transaction.tests.examples.ResourceManagerrUuhTK h h~hhAh X3:class:`transaction.tests.examples.ResourceManager`rVhV]rWh)rX}rY(hgjHh hhV]rZhcX*transaction.tests.examples.ResourceManagerr[r\}r](hgjXh Uubah jVh8}r^(hG]r_(hjSXpy-classr`ehC]rah:]rbh<]rchE]rduubahgj:ubhcX class provides a trivial resource-manager implementation and doc strings to illustrate the protocol and to provide a tool for writing tests.rerf}rg(hgj:h X class provides a trivial resource-manager implementation and doc strings to illustrate the protocol and to provide a tool for writing tests.rhubehgjubh)ri}rj(h8}rk(hG]rlhC]rmh:]rnh<]rohE]rpuh hhTKh hhhAh XoOur sample resource manager has state that is updated through an inc method and through transaction operations.rqhV]rrhcXoOur sample resource manager has state that is updated through an inc method and through transaction operations.rsrt}ru(hgjih jqubahgjubh)rv}rw(h8}rx(hG]ryhC]rzh:]r{h<]r|hE]r}uh hhTKh hhhAh X)When we create a sample resource manager:r~hV]rhcX)When we create a sample resource manager:rr}r(hgjvh j~ubahgjubh)r}r(h8}r(U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTKh hhhAh X>>> rm = ResourceManager()rhV]rhcX>>> rm = ResourceManager()rr}r(hgjh Uubahgjubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hhhAh X@It has two pieces state, state and delta, both initialized to 0:rhV]rhcX@It has two pieces state, state and delta, both initialized to 0:rr}r(hgjh jubahgjubh)r}r(h8}r(U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTKh hhhAh X>>> rm.state 0 >>> rm.delta 0rhV]rhcX>>> rm.state 0 >>> rm.delta 0rr}r(hgjh Uubahgjubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTK"h hhhAh Xstate is meant to model committed state, while delta represents tentative changes within a transaction. We change the state by calling inc:rhV]rhcXstate is meant to model committed state, while delta represents tentative changes within a transaction. We change the state by calling inc:rr}r(hgjh jubahgjubh)r}r(h8}r(U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTK&h hhhAh X >>> rm.inc()rhV]rhcX >>> rm.inc()rr}r(hgjh Uubahgjubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTK*h hhhAh Xwhich updates delta:rhV]rhcXwhich updates delta:rr}r(hgjh jubahgjubh)r}r(h8}r(U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTK,h hhhAh X>>> rm.delta 1rhV]rhcX>>> rm.delta 1rr}r(hgjh Uubahgjubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTK1h hhhAh X8but state isn't changed until we commit the transaction:rhV]rhcX8but state isn't changed until we commit the transaction:rr}r(hgjh jubahgjubh)r}r(h8}r(U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}r hE]r hG]r uh hhTK3h hhhAh X>>> rm.state 0r hV]r hcX>>> rm.state 0rr}r(hgjh Uubahgjubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTK8h hhhAh X>To commit the changes, we use 2-phase commit. We execute the first stage by calling prepare. We need to pass a transation. Our sample resource managers don't really use the transactions for much, so we'll be lazy and use strings for transactions. The sample resource manager updates the state when we call tpc_vote:rhV]rhcX>To commit the changes, we use 2-phase commit. We execute the first stage by calling prepare. We need to pass a transation. Our sample resource managers don't really use the transactions for much, so we'll be lazy and use strings for transactions. The sample resource manager updates the state when we call tpc_vote:rr}r(hgjh jubahgjubh)r}r(h8}r (U testnodetyper!Xdoctestr"Ugroupsr#]r$hah:]r%h<]r&hC]r'hhh}r(hE]r)hG]r*uh hhTK?h hhhAh X>>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.state, rm.delta (0, 1) >>> rm.tpc_vote(t1) >>> rm.state, rm.delta (1, 1) Now if we call tpc_finish: >>> rm.tpc_finish(t1)r+hV]r,hcX>>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.state, rm.delta (0, 1) >>> rm.tpc_vote(t1) >>> rm.state, rm.delta (1, 1) Now if we call tpc_finish: >>> rm.tpc_finish(t1)r-r.}r/(hgjh Uubahgjubh)r0}r1(h8}r2(hG]r3hC]r4h:]r5h<]r6hE]r7uh hhTKNh hhhAh X_Our changes are "permanent". The state reflects the changes and the delta has been reset to 0.r8hV]r9hcX_Our changes are "permanent". The state reflects the changes and the delta has been reset to 0.r:r;}r<(hgj0h j8ubahgjubh)r=}r>(h8}r?(U testnodetyper@XdoctestrAUgroupsrB]rChah:]rDh<]rEhC]rFhhh}rGhE]rHhG]rIuh hhTKQh hhhAh X>>> rm.state, rm.delta (1, 0)rJhV]rKhcX>>> rm.state, rm.delta (1, 0)rLrM}rN(hgj=h Uubahgjubeh UhhAhghubhLhK)rO}rP(h8}rQ(hG]rRhC]rSh:]rThah<]rUhE]rVhauh hhTKh hUhV]rW(hX)rX}rY(h8}rZ(hG]r[hC]r\h:]r]h<]r^hE]r_uh hhTKh hahV]r`(hcXThe rarb}rc(hgjXh XThe rdubhi)re}rf(h8}rg(hmhnhohpNhqNh:]rhh<]riU refexplicitrjUreftyperkXmethrlhC]rmhE]rnU refdomainroXpyrphG]rqh|Xtpc_voterruhTKh h~hhAh X:meth:`tpc_vote`rshV]rth)ru}rv(hgjeh hhV]rwhcX tpc_vote()rxry}rz(hgjuh Uubah jsh8}r{(hG]r|(hjpXpy-methr}ehC]r~h:]rh<]rhE]ruubahgjXubhcX Methodrr}r(hgjXh X Methodrubeh XThe :meth:`tpc_vote` MethodrhhAhgjOubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hhhAh X6Verify that a data manager can commit the transaction.rhV]rhcX6Verify that a data manager can commit the transaction.rr}r(hgjh jubahgjOubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hhhAh XlThis is the last chance for a data manager to vote 'no'. A data manager votes 'no' by raising an exception.rhV]rhcXlThis is the last chance for a data manager to vote 'no'. A data manager votes 'no' by raising an exception.rr}r(hgjh jubahgjOubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hhhAh XiPassed `transaction`, which is the ITransaction instance associated with the transaction being committed.rhV]r(hcXPassed rr}r(hgjh XPassed rubcdocutils.nodes title_reference r)r}r(hgjh Utitle_referencerhV]rhcX transactionrr}r(hgjh Uubah X `transaction`rh8}r(hG]rhC]rh:]rh<]rhE]ruubhcXU, which is the ITransaction instance associated with the transaction being committed.rr}r(hgjh XU, which is the ITransaction instance associated with the transaction being committed.rubehgjOubeh UhhAhghubhK)r}r(h8}r(hG]rhC]rh:]rh ah<]rhE]rhauh hhTKh hUhV]r(hX)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hahV]r(hcXThe rr}r(hgjh XThe rubhi)r}r(h8}r(hmhnhohpNhqNh:]rh<]rU refexplicitrUreftyperXmethrhC]rhE]rU refdomainrXpyrhG]rh|X tpc_finishruhTKh h~hhAh X:meth:`tpc_finish`rhV]rh)r}r(hgjh hhV]rhcX tpc_finish()rr}r(hgjh Uubah jh8}r(hG]r(hjXpy-methrehC]rh:]rh<]rhE]ruubahgjubhcX Methodrr}r(hgjh X Methodrubeh XThe :meth:`tpc_finish` MethodrhhAhgjubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hhhAh XComplete two-phase commitrhV]rhcXComplete two-phase commitrr}r(hgjh jubahgjubh)r}r(h8}r (U testnodetyper Xdoctestr Ugroupsr ]r hah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTKh hhhAh X>>> rm = ResourceManager() >>> rm.state 0 >>> rm.inc() We start two-phase commit by calling prepare: >>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) We complete it by calling tpc_finish: >>> rm.tpc_finish(t1) >>> rm.state 1rhV]rhcX>>> rm = ResourceManager() >>> rm.state 0 >>> rm.inc() We start two-phase commit by calling prepare: >>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) We complete it by calling tpc_finish: >>> rm.tpc_finish(t1) >>> rm.state 1rr}r(hgjh Uubahgjubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]r uh hhTKh hhhAh X;It is an error ro call tpc_finish without calling tpc_vote:r!hV]r"hcX;It is an error ro call tpc_finish without calling tpc_vote:r#r$}r%(hgjh j!ubahgjubh)r&}r'(h8}r((U testnodetyper)Xdoctestr*Ugroupsr+]r,hah:]r-h<]r.hC]r/hhh}r0hE]r1hG]r2uh hhTKh hhhAh X>>> rm.inc() >>> t2 = '2' >>> rm.tpc_begin(t2) >>> rm.tpc_finish(t2) Traceback (most recent call last): ... ValueError: txn in state 'tpc_begin' but expected one of ('tpc_vote',) >>> rm.tpc_abort(t2) # clean slate >>> rm.tpc_begin(t2) >>> rm.tpc_vote(t2) >>> rm.tpc_finish(t2)r3hV]r4hcX>>> rm.inc() >>> t2 = '2' >>> rm.tpc_begin(t2) >>> rm.tpc_finish(t2) Traceback (most recent call last): ... ValueError: txn in state 'tpc_begin' but expected one of ('tpc_vote',) >>> rm.tpc_abort(t2) # clean slate >>> rm.tpc_begin(t2) >>> rm.tpc_vote(t2) >>> rm.tpc_finish(t2)r5r6}r7(hgj&h Uubahgjubh)r8}r9(h8}r:(hG]r;hC]r<h:]r=h<]r>hE]r?uh hhTKh hhhAh XOOf course, the transactions given to tpc_begin and tpc_finish must be the same:r@hV]rAhcXOOf course, the transactions given to tpc_begin and tpc_finish must be the same:rBrC}rD(hgj8h j@ubahgjubh)rE}rF(h8}rG(U testnodetyperHXdoctestrIUgroupsrJ]rKhah:]rLh<]rMhC]rNhhh}rOhE]rPhG]rQuh hhTKh hhhAh X>>> rm.inc() >>> t3 = '3' >>> rm.tpc_begin(t3) >>> rm.tpc_vote(t3) >>> rm.tpc_finish(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '3')rRhV]rShcX>>> rm.inc() >>> t3 = '3' >>> rm.tpc_begin(t3) >>> rm.tpc_vote(t3) >>> rm.tpc_finish(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '3')rTrU}rV(hgjEh Uubahgjubeh UhhAhghubhK)rW}rX(h8}rY(hG]rZhC]r[h:]r\hah<]r]hE]r^hauh hhTKh hUhV]r_(hX)r`}ra(h8}rb(hG]rchC]rdh:]reh<]rfhE]rguh hhTKh hahV]rh(hcXThe rirj}rk(hgj`h XThe rlubhi)rm}rn(h8}ro(hmhnhohpNhqNh:]rph<]rqU refexplicitrrUreftypersXmethrthC]ruhE]rvU refdomainrwXpyrxhG]ryh|X tpc_abortrzuhTKh h~hhAh X:meth:`tpc_abort`r{hV]r|h)r}}r~(hgjmh hhV]rhcX tpc_abort()rr}r(hgj}h Uubah j{h8}r(hG]r(hjxXpy-methrehC]rh:]rh<]rhE]ruubahgj`ubhcX Methodrr}r(hgj`h X Methodrubeh XThe :meth:`tpc_abort` MethodrhhAhgjWubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hhhAh XAbort a transactionrhV]rhcXAbort a transactionrr}r(hgjh jubahgjWubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hhhAh XbThe abort method can be called before two-phase commit to throw away work done in the transaction:rhV]rhcXbThe abort method can be called before two-phase commit to throw away work done in the transaction:rr}r(hgjh jubahgjWubh)r}r(h8}r(U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTKh hhhAh X>>> rm = ResourceManager() >>> rm.inc() >>> rm.state, rm.delta (0, 1) >>> t1 = '1' >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0)rhV]rhcX>>> rm = ResourceManager() >>> rm.inc() >>> rm.state, rm.delta (0, 1) >>> t1 = '1' >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0)rr}r(hgjh UubahgjWubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hhhAh X:The abort method also throws away work done in savepoints:rhV]rhcX:The abort method also throws away work done in savepoints:rr}r(hgjh jubahgjWubh)r}r(h8}r(U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTKh hhhAh X>>> rm.inc() >>> r = rm.savepoint(t1) >>> rm.inc() >>> r = rm.savepoint(t1) >>> rm.state, rm.delta (0, 2) >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0)rhV]rhcX>>> rm.inc() >>> r = rm.savepoint(t1) >>> rm.inc() >>> r = rm.savepoint(t1) >>> rm.state, rm.delta (0, 2) >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0)rr}r(hgjh UubahgjWubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hhhAh XBIf savepoints are used, abort must be passed the same transaction:rhV]rhcXBIf savepoints are used, abort must be passed the same transaction:rr}r(hgjh jubahgjWubh)r}r(h8}r(U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTKh hhhAh X>>> rm.inc() >>> r = rm.savepoint(t1) >>> t2 = '2' >>> rm.tpc_abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_abort(t1)rhV]rhcX>>> rm.inc() >>> r = rm.savepoint(t1) >>> t2 = '2' >>> rm.tpc_abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_abort(t1)rr}r(hgjh UubahgjWubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTKh hhhAh X:The abort method is also used to abort a two-phase commit:rhV]rhcX:The abort method is also used to abort a two-phase commit:rr}r(hgjh jubahgjWubh)r}r(h8}r(U testnodetyper Xdoctestr Ugroupsr ]r hah:]r h<]rhC]rhhh}rhE]rhG]ruh hhTKh hhhAh X>>> rm.inc() >>> rm.state, rm.delta (0, 1) >>> rm.tpc_begin(t1) >>> rm.state, rm.delta (0, 1) >>> rm.tpc_vote(t1) >>> rm.state, rm.delta (1, 1) >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0)rhV]rhcX>>> rm.inc() >>> rm.state, rm.delta (0, 1) >>> rm.tpc_begin(t1) >>> rm.state, rm.delta (0, 1) >>> rm.tpc_vote(t1) >>> rm.state, rm.delta (1, 1) >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0)rr}r(hgjh UubahgjWubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTMh hhhAh XCOf course, the transactions passed to prepare and abort must match:r hV]r!hcXCOf course, the transactions passed to prepare and abort must match:r"r#}r$(hgjh j ubahgjWubh)r%}r&(h8}r'(U testnodetyper(Xdoctestr)Ugroupsr*]r+hah:]r,h<]r-hC]r.hhh}r/hE]r0hG]r1uh hhTMh hhhAh X>>> rm.tpc_begin(t1) >>> rm.tpc_abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_abort(t1)r2hV]r3hcX>>> rm.tpc_begin(t1) >>> rm.tpc_abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_abort(t1)r4r5}r6(hgj%h UubahgjWubh)r7}r8(h8}r9(hG]r:hC]r;h:]r<h<]r=hE]r>uh hhTMh hhhAh XThis should never fail.r?hV]r@hcXThis should never fail.rArB}rC(hgj7h j?ubahgjWubeh UhhAhghubhK)rD}rE(h8}rF(hG]rGhC]rHh:]rIhah<]rJhE]rKhauh hhTMh hUhV]rL(hX)rM}rN(h8}rO(hG]rPhC]rQh:]rRh<]rShE]rTuh hhTMh hahV]rU(hcXThe rVrW}rX(hgjMh XThe rYubhi)rZ}r[(h8}r\(hmhnhohpNhqNh:]r]h<]r^U refexplicitr_Ureftyper`XmethrahC]rbhE]rcU refdomainrdXpyrehG]rfh|X savepointrguhTMh h~hhAh X:meth:`savepoint`rhhV]rih)rj}rk(hgjZh hhV]rlhcX savepoint()rmrn}ro(hgjjh Uubah jhh8}rp(hG]rq(hjeXpy-methrrehC]rsh:]rth<]ruhE]rvuubahgjMubhcX Methodrwrx}ry(hgjMh X Methodrzubeh XThe :meth:`savepoint` Methodr{hhAhgjDubh)r|}r}(h8}r~(hG]rhC]rh:]rh<]rhE]ruh hhTM!h hhhAh X1Provide the ability to rollback transaction staterhV]rhcX1Provide the ability to rollback transaction staterr}r(hgj|h jubahgjDubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTM#h hhhAh XSavepoints provide a way to:rhV]rhcXSavepoints provide a way to:rr}r(hgjh jubahgjDubcdocutils.nodes block_quote r)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTNh U block_quoterhV]rcdocutils.nodes bullet_list r)r}r(hgjh U bullet_listrhV]r(cdocutils.nodes list_item r)r}r(hgjh U list_itemrhV]rh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruhTM%h hhhAh XrSave partial transaction work. For some resource managers, this could allow resources to be used more efficiently.rhV]rhcXrSave partial transaction work. For some resource managers, this could allow resources to be used more efficiently.rr}r(hgjh jubahgjubah XsSave partial transaction work. For some resource managers, this could allow resources to be used more efficiently. rh8}r(hG]rhC]rh:]rh<]rhE]ruubj)r}r(hgjh jhV]rh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruhTM(h hhhAh XProvide the ability to revert state to a point in a transaction without aborting the entire transaction. In other words, savepoints support partial aborts.rhV]rhcXProvide the ability to revert state to a point in a transaction without aborting the entire transaction. In other words, savepoints support partial aborts.rr}r(hgjh jubahgjubah XProvide the ability to revert state to a point in a transaction without aborting the entire transaction. In other words, savepoints support partial aborts. rh8}r(hG]rhC]rh:]rh<]rhE]ruubeh Uh8}r(UbulletrX-h:]rh<]rhC]rhE]rhG]ruubah UhNhgjDubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTM,h hhhAh XSavepoints don't use two-phase commit. If there are errors in setting or rolling back to savepoints, the application should abort the containing transaction. This is *not* the responsibility of the resource manager.rhV]r(hcXSavepoints don't use two-phase commit. If there are errors in setting or rolling back to savepoints, the application should abort the containing transaction. This is rr}r(hgjh XSavepoints don't use two-phase commit. If there are errors in setting or rolling back to savepoints, the application should abort the containing transaction. This is rubcdocutils.nodes emphasis r)r}r(hgjh UemphasisrhV]rhcXnotrr}r(hgjh Uubah X*not*rh8}r(hG]rhC]rh:]rh<]rhE]ruubhcX, the responsibility of the resource manager.rr}r(hgjh X, the responsibility of the resource manager.rubehgjDubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTM1h hhhAh XSavepoints are always associated with a transaction. Any work done in a savepoint's transaction is tentative until the transaction is committed using two-phase commit.rhV]rhcXSavepoints are always associated with a transaction. Any work done in a savepoint's transaction is tentative until the transaction is committed using two-phase commit.rr }r (hgjh jubahgjDubh)r }r (h8}r (U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTM5h hhhAh X">>> rm = ResourceManager() >>> rm.inc() >>> t1 = '1' >>> r = rm.savepoint(t1) >>> rm.state, rm.delta (0, 1) >>> rm.inc() >>> rm.state, rm.delta (0, 2) >>> r.rollback() >>> rm.state, rm.delta (0, 1) >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> rm.state, rm.delta (1, 0)rhV]rhcX">>> rm = ResourceManager() >>> rm.inc() >>> t1 = '1' >>> r = rm.savepoint(t1) >>> rm.state, rm.delta (0, 1) >>> rm.inc() >>> rm.state, rm.delta (0, 2) >>> r.rollback() >>> rm.state, rm.delta (0, 1) >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> rm.state, rm.delta (1, 0)rr}r(hgj h UubahgjDubh)r}r(h8}r(hG]r hC]r!h:]r"h<]r#hE]r$uh hhTMIh hhhAh X*Savepoints must have the same transaction:r%hV]r&hcX*Savepoints must have the same transaction:r'r(}r)(hgjh j%ubahgjDubh)r*}r+(h8}r,(U testnodetyper-Xdoctestr.Ugroupsr/]r0hah:]r1h<]r2hC]r3hhh}r4hE]r5hG]r6uh hhTMKh hhhAh X%>>> r1 = rm.savepoint(t1) >>> rm.state, rm.delta (1, 0) >>> rm.inc() >>> rm.state, rm.delta (1, 1) >>> t2 = '2' >>> r2 = rm.savepoint(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> r2 = rm.savepoint(t1) >>> rm.inc() >>> rm.state, rm.delta (1, 2)r7hV]r8hcX%>>> r1 = rm.savepoint(t1) >>> rm.state, rm.delta (1, 0) >>> rm.inc() >>> rm.state, rm.delta (1, 1) >>> t2 = '2' >>> r2 = rm.savepoint(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> r2 = rm.savepoint(t1) >>> rm.inc() >>> rm.state, rm.delta (1, 2)r9r:}r;(hgj*h UubahgjDubh)r<}r=(h8}r>(hG]r?hC]r@h:]rAh<]rBhE]rCuh hhTM^h hhhAh XGIf we rollback to an earlier savepoint, we discard all work done later:rDhV]rEhcXGIf we rollback to an earlier savepoint, we discard all work done later:rFrG}rH(hgj<h jDubahgjDubh)rI}rJ(h8}rK(U testnodetyperLXdoctestrMUgroupsrN]rOhah:]rPh<]rQhC]rRhhh}rShE]rThG]rUuh hhTMah hhhAh X/>>> r1.rollback() >>> rm.state, rm.delta (1, 0)rVhV]rWhcX/>>> r1.rollback() >>> rm.state, rm.delta (1, 0)rXrY}rZ(hgjIh UubahgjDubh)r[}r\(h8}r](hG]r^hC]r_h:]r`h<]rahE]rbuh hhTMgh hhhAh X5and we can no longer rollback to the later savepoint:rchV]rdhcX5and we can no longer rollback to the later savepoint:rerf}rg(hgj[h jcubahgjDubh)rh}ri(h8}rj(U testnodetyperkXdoctestrlUgroupsrm]rnhah:]roh<]rphC]rqhhh}rrhE]rshG]rtuh hhTMih hhhAh Xx>>> r2.rollback() Traceback (most recent call last): ... TypeError: ('Attempt to roll back to invalid save point', 3, 2)ruhV]rvhcXx>>> r2.rollback() Traceback (most recent call last): ... TypeError: ('Attempt to roll back to invalid save point', 3, 2)rwrx}ry(hgjhh UubahgjDubh)rz}r{(h8}r|(hG]r}hC]r~h:]rh<]rhE]ruh hhTMph hhhAh X4We can roll back to a savepoint as often as we like:rhV]rhcX4We can roll back to a savepoint as often as we like:rr}r(hgjzh jubahgjDubh)r}r(h8}r(U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTMrh hhhAh X>>> r1.rollback() >>> r1.rollback() >>> r1.rollback() >>> rm.state, rm.delta (1, 0) >>> rm.inc() >>> rm.inc() >>> rm.inc() >>> rm.state, rm.delta (1, 3) >>> r1.rollback() >>> rm.state, rm.delta (1, 0)rhV]rhcX>>> r1.rollback() >>> r1.rollback() >>> r1.rollback() >>> rm.state, rm.delta (1, 0) >>> rm.inc() >>> rm.inc() >>> rm.inc() >>> rm.state, rm.delta (1, 3) >>> r1.rollback() >>> rm.state, rm.delta (1, 0)rr}r(hgjh UubahgjDubh)r}r(h8}r(hG]rhC]rh:]rh<]rhE]ruh hhTMh hhhAh XABut we can't rollback to a savepoint after it has been committed:rhV]rhcXABut we can't rollback to a savepoint after it has been committed:rr}r(hgjh jubahgjDubh)r}r(h8}r(U testnodetyperXdoctestrUgroupsr]rhah:]rh<]rhC]rhhh}rhE]rhG]ruh hhTMh hhhAh X>>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> r1.rollback() Traceback (most recent call last): ... TypeError: Attempt to rollback stale rollbackrhV]rhcX>>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> r1.rollback() Traceback (most recent call last): ... TypeError: Attempt to rollback stale rollbackrr}r(hgjh UubahgjDubeh UhhAhghubeh UhhAhghububh!jhjDhhhjWh jhjOuU decorationrNhV]rhaU footnote_refsr}rU current_linerNUsettingsr(cdocutils.frontend Values ror}r(U sectnum_xformrKU halt_levelrKU datestamprNU tab_widthrKU toc_backlinksrUentryrUembed_stylesheetrUgettext_compactrU report_levelrKUstrip_commentsrNU source_linkrNUenvrNU smart_quotesrU_disable_configrNUsectsubtitle_xformrUinput_encoding_error_handlerrUstrictrU rfc_base_urlrUhttp://tools.ietf.org/html/rUfile_insertion_enabledrUdebugrNUwarning_streamrNU strip_classesrNUoutput_encodingrUutf-8rU docinfo_xformrKU source_urlrNUdoctitle_xformrUdump_internalsrNUfootnote_backlinksrKU id_prefixrUUpep_referencesrNhaNUauto_id_prefixrUidrUstrip_elements_with_classesrNUoutput_encoding_error_handlerrjU _config_filesr]rU pep_base_urlrUhttp://www.python.org/dev/peps/rU _destinationrNUerror_encodingrUUTF-8rU tracebackrUrfc_referencesrNUexpose_internalsrNUtrim_footnote_reference_spacerUrecord_dependenciesrNU_sourcerUE/home/tseaver/projects/Zope/ZODB/transaction/docs/resourcemanager.rstrU raw_enabledrKUconfigrNU generatorrNUerror_encoding_error_handlerrUbackslashreplacerU language_coderUenrUinput_encodingrU utf-8-sigrUstrict_visitorrNUsyntax_highlightrUlongrUexit_status_levelrKU dump_settingsrNUcloak_email_addressesrUdump_transformsrNUdump_pseudo_xmlrNUpep_file_url_templaterUpep-%04drubUtransform_messagesr]rUautofootnote_startrKub.transaction-1.4.3/docs/_build/doctrees/datamanager.doctree0000664000175000017500000007764412312641212023577 0ustar tseavertseavercdocutils.nodes document q)q}q(Urefnamesq}qUsubstitution_defsq}qUautofootnote_refsq]q Utagnameq Udocumentq U rawsourceq UU nametypesq }q(Xthe prepare() methodqNXwriting a data managerqNXsimple data managerqNXthe savepoint() methodqNXthe commit() methodqNXthe abort() methodqNuUreporterqNUsymbol_footnote_startqKUid_startqKh hUnameidsq}q(hUthe-prepare-methodqhUwriting-a-data-managerqhUsimple-data-managerqhUthe-savepoint-methodqhUthe-commit-methodqhUthe-abort-methodquUsymbol_footnote_refsq ]q!Uindirect_targetsq"]q#Ucurrent_sourceq$NUparse_messagesq%]q&cdocutils.nodes system_message q')q(}q)(U attributesq*}q+(Utypeq,UWARNINGq-Ulevelq.KUlineq/KUidsq0]q1Udupnamesq2]q3Usourceq4cdocutils.nodes reprunicode q5XA/home/tseaver/projects/Zope/ZODB/transaction/docs/datamanager.rstq6q7}q8bUbackrefsq9]q:Unamesq;]quh hUlineq?Kh Usystem_messageq@UchildrenqA]qB(cdocutils.nodes paragraph qC)qD}qE(UparentqFh(h U paragraphqGhA]qHcdocutils.nodes Text qIXTitle underline too short.qJqK}qL(hFhDh Uubah Uh*}qM(h=]qNh9]qOh0]qPh2]qQh;]qRuubcdocutils.nodes literal_block qS)qT}qU(hFh(h U literal_blockqVhA]qWhIX&Simple Data Manager ------------------qXqY}qZ(hFhTh Uubah X&Simple Data Manager ------------------q[h*}q\(h0]q]h2]q^h9]q_U xml:spaceq`Upreserveqah;]qbh=]qcuubeh UUsourceqdh7hFcdocutils.nodes section qe)qf}qg(h*}qh(h=]qih9]qjh0]qkhah2]qlh;]qmhauh hh?Kh UsectionqnhA]qo(cdocutils.nodes title qp)qq}qr(h*}qs(h=]qth9]quh0]qvh2]qwh;]qxuh hh?Kh UtitleqyhA]qzhIXSimple Data Managerq{q|}q}(hFhqh XSimple Data Managerq~ubah h~hdh7hFhfubhS)q}q(h*}q(U testnodetypeqXdoctestqUgroupsq]qUdefaultqah0]qh2]qh9]qh`haUoptionsq}qh;]qh=]quh hh?Kh hVhdh7h X6>>> from transaction.tests.examples import DataManagerqhA]qhIX6>>> from transaction.tests.examples import DataManagerqq}q(hFhh UubahFhfubhC)q}q(h*}q(h=]qh9]qh0]qh2]qh;]quh hh?K h hGhdh7h XThis :class:`transaction.tests.examples.DataManager` class provides a trivial data-manager implementation and docstrings to illustrate the the protocol and to provide a tool for writing tests.qhA]q(hIXThis qq}q(hFhh XThis qubcsphinx.addnodes pending_xref q)q}q(h*}q(UrefdocqX datamanagerqUrefwarnqU py:moduleqNUpy:classqNh0]qh2]qU refexplicitqUreftypeqXclassqh9]qh;]qU refdomainqXpyqh=]qU reftargetqX&transaction.tests.examples.DataManagerquh?K h U pending_xrefqhdh7h X/:class:`transaction.tests.examples.DataManager`qhA]qcdocutils.nodes literal q)q}q(hFhh UliteralqhA]qhIX&transaction.tests.examples.DataManagerqq}q(hFhh Uubah hh*}q(h=]q(UxrefqhXpy-classqeh9]qh0]qh2]qh;]quubahFhubhIX class provides a trivial data-manager implementation and docstrings to illustrate the the protocol and to provide a tool for writing tests.qɅq}q(hFhh X class provides a trivial data-manager implementation and docstrings to illustrate the the protocol and to provide a tool for writing tests.qubehFhfubhC)q}q(h*}q(h=]qh9]qh0]qh2]qh;]quh hh?Kh hGhdh7h XkOur sample data manager has state that is updated through an inc method and through transaction operations.qhA]qhIXkOur sample data manager has state that is updated through an inc method and through transaction operations.qׅq}q(hFhh hubahFhfubhC)q}q(h*}q(h=]qh9]qh0]qh2]qh;]quh hh?Kh hGhdh7h X%When we create a sample data manager:qhA]qhIX%When we create a sample data manager:q䅁q}q(hFhh hubahFhfubhS)q}q(h*}q(U testnodetypeqXdoctestqUgroupsq]qhah0]qh2]qh9]qh`hah}qh;]qh=]quh hh?Kh hVhdh7h X>>> dm = DataManager()qhA]qhIX>>> dm = DataManager()qq}q(hFhh UubahFhfubhC)q}q(h*}q(h=]qh9]qh0]qh2]qh;]ruh hh?Kh hGhdh7h X It has two bits of state, state:rhA]rhIX It has two bits of state, state:rr}r(hFhh jubahFhfubhS)r}r(h*}r(U testnodetyper Xdoctestr Ugroupsr ]r hah0]r h2]rh9]rh`hah}rh;]rh=]ruh hh?Kh hVhdh7h X>>> dm.state 0rhA]rhIX>>> dm.state 0rr}r(hFjh UubahFhfubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?K h hGhdh7h X and delta:r hA]r!hIX and delta:r"r#}r$(hFjh j ubahFhfubhS)r%}r&(h*}r'(U testnodetyper(Xdoctestr)Ugroupsr*]r+hah0]r,h2]r-h9]r.h`hah}r/h;]r0h=]r1uh hh?K"h hVhdh7h X>>> dm.delta 0r2hA]r3hIX>>> dm.delta 0r4r5}r6(hFj%h UubahFhfubhC)r7}r8(h*}r9(h=]r:h9]r;h0]r<h2]r=h;]r>uh hh?K'h hGhdh7h XBoth of which are initialized to 0. state is meant to model committed state, while delta represents tentative changes within a transaction. We change the state by calling inc:r?hA]r@hIXBoth of which are initialized to 0. state is meant to model committed state, while delta represents tentative changes within a transaction. We change the state by calling inc:rArB}rC(hFj7h j?ubahFhfubhS)rD}rE(h*}rF(U testnodetyperGXdoctestrHUgroupsrI]rJhah0]rKh2]rLh9]rMh`hah}rNh;]rOh=]rPuh hh?K+h hVhdh7h X >>> dm.inc()rQhA]rRhIX >>> dm.inc()rSrT}rU(hFjDh UubahFhfubhC)rV}rW(h*}rX(h=]rYh9]rZh0]r[h2]r\h;]r]uh hh?K/h hGhdh7h Xwhich updates delta:r^hA]r_hIXwhich updates delta:r`ra}rb(hFjVh j^ubahFhfubhS)rc}rd(h*}re(U testnodetyperfXdoctestrgUgroupsrh]rihah0]rjh2]rkh9]rlh`hah}rmh;]rnh=]rouh hh?K1h hVhdh7h X>>> dm.delta 1rphA]rqhIX>>> dm.delta 1rrrs}rt(hFjch UubahFhfubhC)ru}rv(h*}rw(h=]rxh9]ryh0]rzh2]r{h;]r|uh hh?K6h hGhdh7h X8but state isn't changed until we commit the transaction:r}hA]r~hIX8but state isn't changed until we commit the transaction:rr}r(hFjuh j}ubahFhfubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?K8h hVhdh7h X>>> dm.state 0rhA]rhIX>>> dm.state 0rr}r(hFjh UubahFhfubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?K=h hGhdh7h XTo commit the changes, we use 2-phase commit. We execute the first stage by calling prepare. We need to pass a transation. Our sample data managers don't really use the transactions for much, so we'll be lazy and use strings for transactions:rhA]rhIXTo commit the changes, we use 2-phase commit. We execute the first stage by calling prepare. We need to pass a transation. Our sample data managers don't really use the transactions for much, so we'll be lazy and use strings for transactions:rr}r(hFjh jubahFhfubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?KBh hVhdh7h X>>> t1 = '1' >>> dm.prepare(t1)rhA]rhIX>>> t1 = '1' >>> dm.prepare(t1)rr}r(hFjh UubahFhfubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?KGh hGhdh7h X?The sample data manager updates the state when we call prepare:rhA]rhIX?The sample data manager updates the state when we call prepare:rr}r(hFjh jubahFhfubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?KIh hVhdh7h X>>> dm.state 1 >>> dm.delta 1rhA]rhIX>>> dm.state 1 >>> dm.delta 1rr}r(hFjh UubahFhfubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?KPh hGhdh7h XCThis is mainly so we can detect some affect of calling the methods.rhA]rhIXCThis is mainly so we can detect some affect of calling the methods.rr}r(hFjh jubahFhfubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?KRh hGhdh7h XNow if we call commit:rhA]rhIXNow if we call commit:rr}r(hFjh jubahFhfubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?KTh hVhdh7h X>>> dm.commit(t1)rhA]rhIX>>> dm.commit(t1)rr}r(hFjh UubahFhfubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?KXh hGhdh7h X^Our changes are"permanent". The state reflects the changes and the delta has been reset to 0.rhA]rhIX^Our changes are"permanent". The state reflects the changes and the delta has been reset to 0.rr }r (hFjh jubahFhfubhS)r }r (h*}r (U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?K[h hVhdh7h X>>> dm.state 1 >>> dm.delta 0rhA]rhIX>>> dm.state 1 >>> dm.delta 0rr}r(hFj h UubahFhfubeh Uhdh7hFhe)r}r(h*}r(h=]r h9]r!h0]r"hah2]r#h;]r$hauh hh?Kh hnhA]r%(hp)r&}r'(h*}r((h=]r)h9]r*h0]r+h2]r,h;]r-uh hh?Kh hyhA]r.hIXWriting a Data Managerr/r0}r1(hFj&h XWriting a Data Managerr2ubah j2hdh7hFjubhfhe)r3}r4(h*}r5(h=]r6h9]r7h0]r8hah2]r9h;]r:hauh hh?Kch hnhA]r;(hp)r<}r=(h*}r>(h=]r?h9]r@h0]rAh2]rBh;]rCuh hh?Kch hyhA]rD(hIXThe rErF}rG(hFj<h XThe rHubh)rI}rJ(h*}rK(hhhhNhNh0]rLh2]rMU refexplicitrNUreftyperOXmethrPh9]rQh;]rRU refdomainrSXpyrTh=]rUhXpreparerVuh?Kbh hhdh7h X:meth:`prepare`rWhA]rXh)rY}rZ(hFjIh hhA]r[hIX prepare()r\r]}r^(hFjYh Uubah jWh*}r_(h=]r`(hjTXpy-methraeh9]rbh0]rch2]rdh;]reuubahFj<ubhIX Methodrfrg}rh(hFj<h X Methodriubeh XThe :meth:`prepare` Methodrjhdh7hFj3ubhC)rk}rl(h*}rm(h=]rnh9]roh0]rph2]rqh;]rruh hh?Keh hGhdh7h XPrepare to commit datarshA]rthIXPrepare to commit datarurv}rw(hFjkh jsubahFj3ubhS)rx}ry(h*}rz(U testnodetyper{Xdoctestr|Ugroupsr}]r~hah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?Kgh hVhdh7h X>>> dm = DataManager() >>> dm.inc() >>> t1 = '1' >>> dm.prepare(t1) >>> dm.commit(t1) >>> dm.state 1 >>> dm.inc() >>> t2 = '2' >>> dm.prepare(t2) >>> dm.abort(t2) >>> dm.state 1rhA]rhIX>>> dm = DataManager() >>> dm.inc() >>> t1 = '1' >>> dm.prepare(t1) >>> dm.commit(t1) >>> dm.state 1 >>> dm.inc() >>> t2 = '2' >>> dm.prepare(t2) >>> dm.abort(t2) >>> dm.state 1rr}r(hFjxh UubahFj3ubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Kwh hGhdh7h XUIt is en error to call prepare more than once without an intervening commit or abort:rhA]rhIXUIt is en error to call prepare more than once without an intervening commit or abort:rr}r(hFjh jubahFj3ubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?Kzh hVhdh7h X>>> dm.prepare(t1) >>> dm.prepare(t1) Traceback (most recent call last): ... TypeError: Already prepared >>> dm.prepare(t2) Traceback (most recent call last): ... TypeError: Already prepared >>> dm.abort(t1)rhA]rhIX>>> dm.prepare(t1) >>> dm.prepare(t1) Traceback (most recent call last): ... TypeError: Already prepared >>> dm.prepare(t2) Traceback (most recent call last): ... TypeError: Already prepared >>> dm.abort(t1)rr}r(hFjh UubahFj3ubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Kh hGhdh7h X@If there was a preceeding savepoint, the transaction must match:rhA]rhIX@If there was a preceeding savepoint, the transaction must match:rr}r(hFjh jubahFj3ubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?Kh hVhdh7h X>>> rollback = dm.savepoint(t1) >>> dm.prepare(t2) Traceback (most recent call last): ,,, TypeError: ('Transaction missmatch', '2', '1') >>> dm.prepare(t1)rhA]rhIX>>> rollback = dm.savepoint(t1) >>> dm.prepare(t2) Traceback (most recent call last): ,,, TypeError: ('Transaction missmatch', '2', '1') >>> dm.prepare(t1)rr}r(hFjh UubahFj3ubeh Uhdh7hFjubhe)r}r(h*}r(h=]rh9]rh0]rhah2]rh;]rhauh hh?Kh hnhA]r(hp)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Kh hyhA]r(hIXThe rr}r(hFjh XThe rubh)r}r(h*}r(hhhhNhNh0]rh2]rU refexplicitrUreftyperXmethrh9]rh;]rU refdomainrXpyrh=]rhXabortruh?Kh hhdh7h X :meth:`abort`rhA]rh)r}r(hFjh hhA]rhIXabort()rr}r(hFjh Uubah jh*}r(h=]r(hjXpy-methreh9]rh0]rh2]rh;]ruubahFjubhIX methodrr}r(hFjh X methodrubeh XThe :meth:`abort` methodrhdh7hFjubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Kh hGhdh7h XbThe abort method can be called before two-phase commit to throw away work done in the transaction:rhA]r hIXbThe abort method can be called before two-phase commit to throw away work done in the transaction:r r }r (hFjh jubahFjubhS)r }r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?Kh hVhdh7h X}>>> dm = DataManager() >>> dm.inc() >>> dm.state, dm.delta (0, 1) >>> t1 = '1' >>> dm.abort(t1) >>> dm.state, dm.delta (0, 0)rhA]rhIX}>>> dm = DataManager() >>> dm.inc() >>> dm.state, dm.delta (0, 1) >>> t1 = '1' >>> dm.abort(t1) >>> dm.state, dm.delta (0, 0)rr}r(hFj h UubahFjubhC)r}r (h*}r!(h=]r"h9]r#h0]r$h2]r%h;]r&uh hh?Kh hGhdh7h X:The abort method also throws away work done in savepoints:r'hA]r(hIX:The abort method also throws away work done in savepoints:r)r*}r+(hFjh j'ubahFjubhS)r,}r-(h*}r.(U testnodetyper/Xdoctestr0Ugroupsr1]r2hah0]r3h2]r4h9]r5h`hah}r6h;]r7h=]r8uh hh?Kh hVhdh7h X>>> dm.inc() >>> r = dm.savepoint(t1) >>> dm.inc() >>> r = dm.savepoint(t1) >>> dm.state, dm.delta (0, 2) >>> dm.abort(t1) >>> dm.state, dm.delta (0, 0)r9hA]r:hIX>>> dm.inc() >>> r = dm.savepoint(t1) >>> dm.inc() >>> r = dm.savepoint(t1) >>> dm.state, dm.delta (0, 2) >>> dm.abort(t1) >>> dm.state, dm.delta (0, 0)r;r<}r=(hFj,h UubahFjubhC)r>}r?(h*}r@(h=]rAh9]rBh0]rCh2]rDh;]rEuh hh?Kh hGhdh7h XBIf savepoints are used, abort must be passed the same transaction:rFhA]rGhIXBIf savepoints are used, abort must be passed the same transaction:rHrI}rJ(hFj>h jFubahFjubhS)rK}rL(h*}rM(U testnodetyperNXdoctestrOUgroupsrP]rQhah0]rRh2]rSh9]rTh`hah}rUh;]rVh=]rWuh hh?Kh hVhdh7h X>>> dm.inc() >>> r = dm.savepoint(t1) >>> t2 = '2' >>> dm.abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> dm.abort(t1)rXhA]rYhIX>>> dm.inc() >>> r = dm.savepoint(t1) >>> t2 = '2' >>> dm.abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> dm.abort(t1)rZr[}r\(hFjKh UubahFjubhC)r]}r^(h*}r_(h=]r`h9]rah0]rbh2]rch;]rduh hh?Kh hGhdh7h X:The abort method is also used to abort a two-phase commit:rehA]rfhIX:The abort method is also used to abort a two-phase commit:rgrh}ri(hFj]h jeubahFjubhS)rj}rk(h*}rl(U testnodetypermXdoctestrnUgroupsro]rphah0]rqh2]rrh9]rsh`hah}rth;]ruh=]rvuh hh?Kh hVhdh7h X>>> dm.inc() >>> dm.state, dm.delta (0, 1) >>> dm.prepare(t1) >>> dm.state, dm.delta (1, 1) >>> dm.abort(t1) >>> dm.state, dm.delta (0, 0)rwhA]rxhIX>>> dm.inc() >>> dm.state, dm.delta (0, 1) >>> dm.prepare(t1) >>> dm.state, dm.delta (1, 1) >>> dm.abort(t1) >>> dm.state, dm.delta (0, 0)ryrz}r{(hFjjh UubahFjubhC)r|}r}(h*}r~(h=]rh9]rh0]rh2]rh;]ruh hh?Kh hGhdh7h XCOf course, the transactions passed to prepare and abort must match:rhA]rhIXCOf course, the transactions passed to prepare and abort must match:rr}r(hFj|h jubahFjubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?Kh hVhdh7h X>>> dm.prepare(t1) >>> dm.abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> dm.abort(t1)rhA]rhIX>>> dm.prepare(t1) >>> dm.abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> dm.abort(t1)rr}r(hFjh UubahFjubeh Uhdh7hFjubhe)r}r(h*}r(h=]rh9]rh0]rhah2]rh;]rhauh hh?Kh hnhA]r(hp)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Kh hyhA]r(hIXThe rr}r(hFjh XThe rubh)r}r(h*}r(hhhhNhNh0]rh2]rU refexplicitrUreftyperXmethrh9]rh;]rU refdomainrXpyrh=]rhXcommitruh?Kh hhdh7h X:meth:`commit`rhA]rh)r}r(hFjh hhA]rhIXcommit()rr}r(hFjh Uubah jh*}r(h=]r(hjXpy-methreh9]rh0]rh2]rh;]ruubahFjubhIX methodrr}r(hFjh X methodrubeh XThe :meth:`commit` methodrhdh7hFjubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Kh hGhdh7h X"Called to omplete two-phase commitrhA]rhIX"Called to omplete two-phase commitrr}r(hFjh jubahFjubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?Kh hVhdh7h X2>>> dm = DataManager() >>> dm.state 0 >>> dm.inc()rhA]rhIX2>>> dm = DataManager() >>> dm.state 0 >>> dm.inc()rr}r(hFjh UubahFjubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Kh hGhdh7h X-We start two-phase commit by calling prepare:rhA]rhIX-We start two-phase commit by calling prepare:rr}r(hFjh jubahFjubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}r h;]r h=]r uh hh?Kh hVhdh7h XB>>> t1 = '1' >>> dm.prepare(t1) We complete it by calling commit:r hA]r hIXB>>> t1 = '1' >>> dm.prepare(t1) We complete it by calling commit:rr}r(hFjh UubahFjubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?Kh hVhdh7h X >>> dm.commit(t1) >>> dm.state 1rhA]rhIX >>> dm.commit(t1) >>> dm.state 1r r!}r"(hFjh UubahFjubhC)r#}r$(h*}r%(h=]r&h9]r'h0]r(h2]r)h;]r*uh hh?Kh hGhdh7h X<It is an error ro call commit without calling prepare first:r+hA]r,hIX<It is an error ro call commit without calling prepare first:r-r.}r/(hFj#h j+ubahFjubhS)r0}r1(h*}r2(U testnodetyper3Xdoctestr4Ugroupsr5]r6hah0]r7h2]r8h9]r9h`hah}r:h;]r;h=]r<uh hh?Kh hVhdh7h X>>> dm.inc() >>> t2 = '2' >>> dm.commit(t2) Traceback (most recent call last): ... TypeError: Not prepared to commit >>> dm.prepare(t2) >>> dm.commit(t2)r=hA]r>hIX>>> dm.inc() >>> t2 = '2' >>> dm.commit(t2) Traceback (most recent call last): ... TypeError: Not prepared to commit >>> dm.prepare(t2) >>> dm.commit(t2)r?r@}rA(hFj0h UubahFjubhC)rB}rC(h*}rD(h=]rEh9]rFh0]rGh2]rHh;]rIuh hh?M h hGhdh7h XIIf course, the transactions given to prepare and commit must be the same:rJhA]rKhIXIIf course, the transactions given to prepare and commit must be the same:rLrM}rN(hFjBh jJubahFjubhS)rO}rP(h*}rQ(U testnodetyperRXdoctestrSUgroupsrT]rUhah0]rVh2]rWh9]rXh`hah}rYh;]rZh=]r[uh hh?M h hVhdh7h X>>> dm.inc() >>> t3 = '3' >>> dm.prepare(t3) >>> dm.commit(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '3')r\hA]r]hIX>>> dm.inc() >>> t3 = '3' >>> dm.prepare(t3) >>> dm.commit(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '3')r^r_}r`(hFjOh UubahFjubeh Uhdh7hFjubhe)ra}rb(h*}rc(h=]rdh9]reh0]rfhah2]rgh;]rhhauh hh?Mh hnhA]ri(hp)rj}rk(h*}rl(h=]rmh9]rnh0]roh2]rph;]rquh hh?Mh hyhA]rr(hIXThe rsrt}ru(hFjjh XThe rvubh)rw}rx(h*}ry(hhhhNhNh0]rzh2]r{U refexplicitr|Ureftyper}Xmethr~h9]rh;]rU refdomainrXpyrh=]rhX savepointruh?Mh hhdh7h X:meth:`savepoint`rhA]rh)r}r(hFjwh hhA]rhIX savepoint()rr}r(hFjh Uubah jh*}r(h=]r(hjXpy-methreh9]rh0]rh2]rh;]ruubahFjjubhIX methodrr}r(hFjjh X methodrubeh XThe :meth:`savepoint` methodrhdh7hFjaubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Mh hGhdh7h X1Provide the ability to rollback transaction staterhA]rhIX1Provide the ability to rollback transaction staterr}r(hFjh jubahFjaubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Mh hGhdh7h XSavepoints provide a way to:rhA]rhIXSavepoints provide a way to:rr}r(hFjh jubahFjaubcdocutils.nodes block_quote r)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Nh U block_quoterhA]rcdocutils.nodes bullet_list r)r}r(hFjh U bullet_listrhA]r(cdocutils.nodes list_item r)r}r(hFjh U list_itemrhA]rhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh?Mh hGhdh7h XnSave partial transaction work. For some data managers, this could allow resources to be used more efficiently.rhA]rhIXnSave partial transaction work. For some data managers, this could allow resources to be used more efficiently.rr}r(hFjh jubahFjubah XoSave partial transaction work. For some data managers, this could allow resources to be used more efficiently. rh*}r(h=]rh9]rh0]rh2]rh;]ruubj)r}r(hFjh jhA]rhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh?M"h hGhdh7h XProvide the ability to revert state to a point in a transaction without aborting the entire transaction. In other words, savepoints support partial aborts.rhA]rhIXProvide the ability to revert state to a point in a transaction without aborting the entire transaction. In other words, savepoints support partial aborts.rr}r(hFjh jubahFjubah XProvide the ability to revert state to a point in a transaction without aborting the entire transaction. In other words, savepoints support partial aborts. rh*}r(h=]rh9]rh0]rh2]rh;]ruubeh Uh*}r(UbulletrX-h0]rh2]rh9]rh;]rh=]ruubah UhdNhFjaubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?M&h hGhdh7h XSavepoints don't use two-phase commit. If there are errors in setting or rolling back to savepoints, the application should abort the containing transaction. This is *not* the responsibility of the data manager.rhA]r(hIXSavepoints don't use two-phase commit. If there are errors in setting or rolling back to savepoints, the application should abort the containing transaction. This is rr}r(hFjh XSavepoints don't use two-phase commit. If there are errors in setting or rolling back to savepoints, the application should abort the containing transaction. This is rubcdocutils.nodes emphasis r)r }r (hFjh Uemphasisr hA]r hIXnotr r}r(hFj h Uubah X*not*rh*}r(h=]rh9]rh0]rh2]rh;]ruubhIX( the responsibility of the data manager.rr}r(hFjh X( the responsibility of the data manager.rubehFjaubhC)r}r(h*}r(h=]rh9]rh0]r h2]r!h;]r"uh hh?M+h hGhdh7h XSavepoints are always associated with a transaction. Any work done in a savepoint's transaction is tentative until the transaction is committed using two-phase commit.r#hA]r$hIXSavepoints are always associated with a transaction. Any work done in a savepoint's transaction is tentative until the transaction is committed using two-phase commit.r%r&}r'(hFjh j#ubahFjaubhS)r(}r)(h*}r*(U testnodetyper+Xdoctestr,Ugroupsr-]r.hah0]r/h2]r0h9]r1h`hah}r2h;]r3h=]r4uh hh?M/h hVhdh7h X>>> dm = DataManager() >>> dm.inc() >>> t1 = '1' >>> r = dm.savepoint(t1) >>> dm.state, dm.delta (0, 1) >>> dm.inc() >>> dm.state, dm.delta (0, 2) >>> r.rollback() >>> dm.state, dm.delta (0, 1) >>> dm.prepare(t1) >>> dm.commit(t1) >>> dm.state, dm.delta (1, 0)r5hA]r6hIX>>> dm = DataManager() >>> dm.inc() >>> t1 = '1' >>> r = dm.savepoint(t1) >>> dm.state, dm.delta (0, 1) >>> dm.inc() >>> dm.state, dm.delta (0, 2) >>> r.rollback() >>> dm.state, dm.delta (0, 1) >>> dm.prepare(t1) >>> dm.commit(t1) >>> dm.state, dm.delta (1, 0)r7r8}r9(hFj(h UubahFjaubhC)r:}r;(h*}r<(h=]r=h9]r>h0]r?h2]r@h;]rAuh hh?MBh hGhdh7h X*Savepoints must have the same transaction:rBhA]rChIX*Savepoints must have the same transaction:rDrE}rF(hFj:h jBubahFjaubhS)rG}rH(h*}rI(U testnodetyperJXdoctestrKUgroupsrL]rMhah0]rNh2]rOh9]rPh`hah}rQh;]rRh=]rSuh hh?MDh hVhdh7h X%>>> r1 = dm.savepoint(t1) >>> dm.state, dm.delta (1, 0) >>> dm.inc() >>> dm.state, dm.delta (1, 1) >>> t2 = '2' >>> r2 = dm.savepoint(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> r2 = dm.savepoint(t1) >>> dm.inc() >>> dm.state, dm.delta (1, 2)rThA]rUhIX%>>> r1 = dm.savepoint(t1) >>> dm.state, dm.delta (1, 0) >>> dm.inc() >>> dm.state, dm.delta (1, 1) >>> t2 = '2' >>> r2 = dm.savepoint(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> r2 = dm.savepoint(t1) >>> dm.inc() >>> dm.state, dm.delta (1, 2)rVrW}rX(hFjGh UubahFjaubhC)rY}rZ(h*}r[(h=]r\h9]r]h0]r^h2]r_h;]r`uh hh?MWh hGhdh7h XGIf we rollback to an earlier savepoint, we discard all work done later:rahA]rbhIXGIf we rollback to an earlier savepoint, we discard all work done later:rcrd}re(hFjYh jaubahFjaubhS)rf}rg(h*}rh(U testnodetyperiXdoctestrjUgroupsrk]rlhah0]rmh2]rnh9]roh`hah}rph;]rqh=]rruh hh?MZh hVhdh7h X/>>> r1.rollback() >>> dm.state, dm.delta (1, 0)rshA]rthIX/>>> r1.rollback() >>> dm.state, dm.delta (1, 0)rurv}rw(hFjfh UubahFjaubhC)rx}ry(h*}rz(h=]r{h9]r|h0]r}h2]r~h;]ruh hh?M`h hGhdh7h X5and we can no longer rollback to the later savepoint:rhA]rhIX5and we can no longer rollback to the later savepoint:rr}r(hFjxh jubahFjaubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?Mbh hVhdh7h Xx>>> r2.rollback() Traceback (most recent call last): ... TypeError: ('Attempt to roll back to invalid save point', 3, 2)rhA]rhIXx>>> r2.rollback() Traceback (most recent call last): ... TypeError: ('Attempt to roll back to invalid save point', 3, 2)rr}r(hFjh UubahFjaubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?Mih hGhdh7h X4We can roll back to a savepoint as often as we like:rhA]rhIX4We can roll back to a savepoint as often as we like:rr}r(hFjh jubahFjaubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?Mkh hVhdh7h X>>> r1.rollback() >>> r1.rollback() >>> r1.rollback() >>> dm.state, dm.delta (1, 0) >>> dm.inc() >>> dm.inc() >>> dm.inc() >>> dm.state, dm.delta (1, 3) >>> r1.rollback() >>> dm.state, dm.delta (1, 0)rhA]rhIX>>> r1.rollback() >>> r1.rollback() >>> r1.rollback() >>> dm.state, dm.delta (1, 0) >>> dm.inc() >>> dm.inc() >>> dm.inc() >>> dm.state, dm.delta (1, 3) >>> r1.rollback() >>> dm.state, dm.delta (1, 0)rr}r(hFjh UubahFjaubhC)r}r(h*}r(h=]rh9]rh0]rh2]rh;]ruh hh?M|h hGhdh7h XABut we can't rollback to a savepoint after it has been committed:rhA]rhIXABut we can't rollback to a savepoint after it has been committed:rr}r(hFjh jubahFjaubhS)r}r(h*}r(U testnodetyperXdoctestrUgroupsr]rhah0]rh2]rh9]rh`hah}rh;]rh=]ruh hh?Mh hVhdh7h X>>> dm.prepare(t1) >>> dm.commit(t1) >>> r1.rollback() Traceback (most recent call last): ... TypeError: Attempt to rollback stale rollbackrhA]rhIX>>> dm.prepare(t1) >>> dm.commit(t1) >>> r1.rollback() Traceback (most recent call last): ... TypeError: Attempt to rollback stale rollbackrr}r(hFjh UubahFjaubeh Uhdh7hFjubeh Uhdh7hFhubububaU autofootnotesr]rUrefidsr}rU transformerrNUsymbol_footnotesr]rU footnotesr]rU citation_refsr}rUsubstitution_namesr}rU citationsr]rh*}r(h0]rh2]rUsourcerh7h9]rh;]rh=]ruUidsr}r(hhfhjhjhjahj3hjuU decorationrNhA]rjaU footnote_refsr}rU current_linerNUsettingsr(cdocutils.frontend Values ror}r(U sectnum_xformrKU halt_levelrKU datestamprNU tab_widthrKU toc_backlinksrUentryrUembed_stylesheetrUgettext_compactrU report_levelrKUstrip_commentsrNU source_linkrNUenvrNU smart_quotesrU_disable_configrNUsectsubtitle_xformrUinput_encoding_error_handlerrUstrictrU rfc_base_urlrUhttp://tools.ietf.org/html/rUfile_insertion_enabledr Udebugr NUwarning_streamr NU strip_classesr NUoutput_encodingr Uutf-8rU docinfo_xformrKU source_urlrNUdoctitle_xformrUdump_internalsrNUfootnote_backlinksrKU id_prefixrUUpep_referencesrNhyNUauto_id_prefixrUidrUstrip_elements_with_classesrNUoutput_encoding_error_handlerrjU _config_filesr]rU pep_base_urlrUhttp://www.python.org/dev/peps/rU _destinationrNUerror_encodingrUUTF-8r U tracebackr!Urfc_referencesr"NUexpose_internalsr#NUtrim_footnote_reference_spacer$Urecord_dependenciesr%NU_sourcer&UA/home/tseaver/projects/Zope/ZODB/transaction/docs/datamanager.rstr'U raw_enabledr(KUconfigr)NU generatorr*NUerror_encoding_error_handlerr+Ubackslashreplacer,U language_coder-Uenr.Uinput_encodingr/U utf-8-sigr0Ustrict_visitorr1NUsyntax_highlightr2Ulongr3Uexit_status_levelr4KU dump_settingsr5NUcloak_email_addressesr6Udump_transformsr7NUdump_pseudo_xmlr8NUpep_file_url_templater9Upep-%04dr:ubUtransform_messagesr;]r<Uautofootnote_startr=Kub.transaction-1.4.3/docs/_build/doctrees/convenience.doctree0000664000175000017500000004270412312641212023614 0ustar tseavertseavercdocutils.nodes document q)q}q(Urefnamesq}qUsubstitution_defsq}qUautofootnote_refsq]q Utagnameq Udocumentq U rawsourceq UU nametypesq }q(XretriesqNXtransaction convenience supportqNX with supportqNuUreporterqNUsymbol_footnote_startqKUid_startqKh hUnameidsq}q(hUretriesqhUtransaction-convenience-supportqhU with-supportquUsymbol_footnote_refsq]qUindirect_targetsq]qUcurrent_sourceqNUparse_messagesq]q U autofootnotesq!]q"Urefidsq#}q$U transformerq%NUsymbol_footnotesq&]q'U footnotesq(]q)U citation_refsq*}q+Usubstitution_namesq,}q-U citationsq.]q/U attributesq0}q1(Uidsq2]q3Udupnamesq4]q5Usourceq6cdocutils.nodes reprunicode q7XA/home/tseaver/projects/Zope/ZODB/transaction/docs/convenience.rstq8q9}q:bUbackrefsq;]qUclassesq?]q@uUidsqA}qB(hcdocutils.nodes section qC)qD}qE(h0}qF(h?]qGh;]qHh2]qIhah4]qJh=]qKhauh hUlineqLKh UsectionqMUchildrenqN]qO(cdocutils.nodes title qP)qQ}qR(h0}qS(h?]qTh;]qUh2]qVh4]qWh=]qXuh hhLKh UtitleqYhN]qZcdocutils.nodes Text q[XTransaction convenience supportq\q]}q^(Uparentq_hQh XTransaction convenience supportq`ubah h`Usourceqah9h_hDubcdocutils.nodes definition_list qb)qc}qd(h0}qe(h?]qfh;]qgh2]qhh4]qih=]qjuh hhLNh Udefinition_listqkhN]qlcdocutils.nodes definition_list_item qm)qn}qo(h0}qp(h?]qqh;]qrh2]qsh4]qth=]quuhLKh Udefinition_list_itemqvhah9h X(We *really* need to write proper documentation for the transaction package, but I don't want to block the conveniences documented here for that.) qwhN]qx(cdocutils.nodes term qy)qz}q{(h0}q|(h?]q}h;]q~h2]qh4]qh=]quhLKh Utermqhah9h XC(We *really* need to write proper documentation for the transactionqhN]q(h[X(We qq}q(h_hzh X(We qubcdocutils.nodes emphasis q)q}q(h_hzh UemphasisqhN]qh[Xreallyqq}q(h_hh Uubah X*really*qh0}q(h?]qh;]qh2]qh4]qh=]quubh[X7 need to write proper documentation for the transactionqq}q(h_hzh X7 need to write proper documentation for the transactionqubeh_hnubcdocutils.nodes definition q)q}q(h_hnh U definitionqhN]qcdocutils.nodes paragraph q)q}q(h0}q(h?]qh;]qh2]qh4]qh=]quhLKh U paragraphqhah9h XNpackage, but I don't want to block the conveniences documented here for that.)qhN]qh[XNpackage, but I don't want to block the conveniences documented here for that.)qq}q(h_hh hubah_hubah Uh0}q(h?]qh;]qh2]qh4]qh=]quubeh_hcubah Uhah9h_hDubhC)q}q(h0}q(h?]qh;]qh2]qhah4]qh=]qhauh hhLK h hMhN]q(hP)q}q(h0}q(h?]qh;]qh2]qh4]qh=]quh hhLK h hYhN]qh[X with supportqȅq}q(h_hh X with supportqubah hhah9h_hubh)q}q(h0}q(h?]qh;]qh2]qh4]qh=]quh hhLK h hhah9h XCWe can now use the with statement to define transaction boundaries.qhN]qh[XCWe can now use the with statement to define transaction boundaries.qօq}q(h_hh hubah_hubcdocutils.nodes literal_block q)q}q(h0}q(U testnodetypeqXdoctestqUgroupsq]qUdefaultqah2]qh4]qh;]qU xml:spaceqUpreserveqUoptionsq}qh=]qh?]quh hhLK h U literal_blockqhah9h X>>> import transaction.tests.savepointsample >>> dm = transaction.tests.savepointsample.SampleSavepointDataManager() >>> list(dm.keys()) []qhN]qh[X>>> import transaction.tests.savepointsample >>> dm = transaction.tests.savepointsample.SampleSavepointDataManager() >>> list(dm.keys()) []qq}q(h_hh Uubah_hubh)q}q(h0}q(h?]qh;]qh2]qh4]qh=]quh hhLKh hhah9h XWe can use it with a manager:qhN]qh[XWe can use it with a manager:qq}q(h_hh hubah_hubh)q}q(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]r h?]r uh hhLKh hhah9h X)>>> with transaction.manager as t: ... dm['z'] = 3 ... t.note('test 3') >>> dm['z'] 3 >>> dm.last_note 'test 3' >>> with transaction.manager: #doctest ELLIPSIS ... dm['z'] = 4 ... xxx Traceback (most recent call last): ... NameError: ... name 'xxx' is not defined >>> dm['z'] 3r hN]r h[X)>>> with transaction.manager as t: ... dm['z'] = 3 ... t.note('test 3') >>> dm['z'] 3 >>> dm.last_note 'test 3' >>> with transaction.manager: #doctest ELLIPSIS ... dm['z'] = 4 ... xxx Traceback (most recent call last): ... NameError: ... name 'xxx' is not defined >>> dm['z'] 3r r}r(h_hh Uubah_hubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLK,h hhah9h XOn Python 2, you can also abbreviate ``with transaction.manager:`` as ``with transaction:``. This does not work on Python 3 (see see http://bugs.python.org/issue12022).rhN]r(h[X%On Python 2, you can also abbreviate rr}r(h_jh X%On Python 2, you can also abbreviate rubcdocutils.nodes literal r)r}r (h_jh Uliteralr!hN]r"h[Xwith transaction.manager:r#r$}r%(h_jh Uubah X``with transaction.manager:``r&h0}r'(h?]r(h;]r)h2]r*h4]r+h=]r,uubh[X as r-r.}r/(h_jh X as r0ubj)r1}r2(h_jh j!hN]r3h[Xwith transaction:r4r5}r6(h_j1h Uubah X``with transaction:``r7h0}r8(h?]r9h;]r:h2]r;h4]r<h=]r=uubh[X+. This does not work on Python 3 (see see r>r?}r@(h_jh X+. This does not work on Python 3 (see see rAubcdocutils.nodes reference rB)rC}rD(h_jh U referencerEhN]rFh[X!http://bugs.python.org/issue12022rGrH}rI(h_jCh Uubah X!http://bugs.python.org/issue12022rJh0}rK(h2]rLh4]rMh=]rNh;]rOh?]rPUrefurirQX!http://bugs.python.org/issue12022rRuubh[X).rSrT}rU(h_jh X).rVubeh_hubeh Uhah9h_hDubhC)rW}rX(h0}rY(h?]rZh;]r[h2]r\hah4]r]h=]r^hauh hhLK1h hMhN]r_(hP)r`}ra(h0}rb(h?]rch;]rdh2]reh4]rfh=]rguh hhLK1h hYhN]rhh[XRetriesrirj}rk(h_j`h XRetriesrlubah jlhah9h_jWubh)rm}rn(h0}ro(h?]rph;]rqh2]rrh4]rsh=]rtuh hhLK3h hhah9h XCommits can fail for transient reasons, especially conflicts. Applications will often retry transactions some number of times to overcome transient failures. This typically looks something like:ruhN]rvh[XCommits can fail for transient reasons, especially conflicts. Applications will often retry transactions some number of times to overcome transient failures. This typically looks something like:rwrx}ry(h_jmh juubah_jWubh)rz}r{(h0}r|(U testnodetyper}Xdoctestr~Ugroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLK7h hhah9h Xfor i in range(3): try: with transaction.manager: ... some something ... except SomeTransientException: contine else: breakrhN]rh[Xfor i in range(3): try: with transaction.manager: ... some something ... except SomeTransientException: contine else: breakrr}r(h_jzh Uubah_jWubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKBh hhah9h XThis is rather ugly.rhN]rh[XThis is rather ugly.rr}r(h_jh jubah_jWubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKDh hhah9h XaTransaction managers provide a helper for this case. To show this, we'll use a contrived example:rhN]rh[XaTransaction managers provide a helper for this case. To show this, we'll use a contrived example:rr}r(h_jh jubah_jWubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLKGh hhah9h X>>> ntry = 0 >>> with transaction.manager: ... dm['ntry'] = 0 >>> import transaction.interfaces >>> class Retry(transaction.interfaces.TransientError): ... pass >>> for attempt in transaction.manager.attempts(): ... with attempt as t: ... t.note('test') ... print("%s %s" % (dm['ntry'], ntry)) ... ntry += 1 ... dm['ntry'] = ntry ... if ntry % 3: ... raise Retry(ntry) 0 0 0 1 0 2rhN]rh[X>>> ntry = 0 >>> with transaction.manager: ... dm['ntry'] = 0 >>> import transaction.interfaces >>> class Retry(transaction.interfaces.TransientError): ... pass >>> for attempt in transaction.manager.attempts(): ... with attempt as t: ... t.note('test') ... print("%s %s" % (dm['ntry'], ntry)) ... ntry += 1 ... dm['ntry'] = ntry ... if ntry % 3: ... raise Retry(ntry) 0 0 0 1 0 2rr}r(h_jh Uubah_jWubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLK]h hhah9h XThe raising of a subclass of TransientError is critical here. It's what signals that the transaction should be retried. It is generally up to the data manager to signal that a transaction should try again by raising a subclass of TransientError (or TransientError itself, of course).rhN]rh[XThe raising of a subclass of TransientError is critical here. It's what signals that the transaction should be retried. It is generally up to the data manager to signal that a transaction should try again by raising a subclass of TransientError (or TransientError itself, of course).rr}r(h_jh jubah_jWubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKch hhah9h X You shouldn't make any assumptions about the object returned by the iterator. (It isn't a transaction or transaction manager, as far as you know. :) If you use the ``as`` keyword in the ``with`` statement, a transaction object will be assigned to the variable named.rhN]r(h[XYou shouldn't make any assumptions about the object returned by the iterator. (It isn't a transaction or transaction manager, as far as you know. :) If you use the rr}r(h_jh XYou shouldn't make any assumptions about the object returned by the iterator. (It isn't a transaction or transaction manager, as far as you know. :) If you use the rubj)r}r(h_jh j!hN]rh[Xasrr}r(h_jh Uubah X``as``rh0}r(h?]rh;]rh2]rh4]rh=]ruubh[X keyword in the rr}r(h_jh X keyword in the rubj)r}r(h_jh j!hN]rh[Xwithrr}r(h_jh Uubah X``with``rh0}r(h?]rh;]rh2]rh4]rh=]ruubh[XH statement, a transaction object will be assigned to the variable named.rr}r(h_jh XH statement, a transaction object will be assigned to the variable named.rubeh_jWubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKhh hhah9h XCBy default, it tries 3 times. We can tell it how many times to try:rhN]rh[XCBy default, it tries 3 times. We can tell it how many times to try:rr}r(h_jh jubah_jWubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]r h4]r h;]r hhh}r h=]r h?]ruh hhLKjh hhah9h X>>> for attempt in transaction.manager.attempts(2): ... with attempt: ... ntry += 1 ... if ntry % 3: ... raise Retry(ntry) Traceback (most recent call last): ... Retry: 5rhN]rh[X>>> for attempt in transaction.manager.attempts(2): ... with attempt: ... ntry += 1 ... if ntry % 3: ... raise Retry(ntry) Traceback (most recent call last): ... Retry: 5rr}r(h_jh Uubah_jWubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKuh hhah9h XKIt it doesn't succeed in that many times, the exception will be propagated.rhN]rh[XKIt it doesn't succeed in that many times, the exception will be propagated.rr}r (h_jh jubah_jWubh)r!}r"(h0}r#(h?]r$h;]r%h2]r&h4]r'h=]r(uh hhLKxh hhah9h X0Of course, other errors are propagated directly:r)hN]r*h[X0Of course, other errors are propagated directly:r+r,}r-(h_j!h j)ubah_jWubh)r.}r/(h0}r0(U testnodetyper1Xdoctestr2Ugroupsr3]r4hah2]r5h4]r6h;]r7hhh}r8h=]r9h?]r:uh hhLKzh hhah9h X>>> ntry = 0 >>> for attempt in transaction.manager.attempts(): ... with attempt: ... ntry += 1 ... if ntry == 3: ... raise ValueError(ntry) Traceback (most recent call last): ... ValueError: 3r;hN]r<h[X>>> ntry = 0 >>> for attempt in transaction.manager.attempts(): ... with attempt: ... ntry += 1 ... if ntry == 3: ... raise ValueError(ntry) Traceback (most recent call last): ... ValueError: 3r=r>}r?(h_j.h Uubah_jWubh)r@}rA(h0}rB(h?]rCh;]rDh2]rEh4]rFh=]rGuh hhLKh hhah9h X+We can use the default transaction manager:rHhN]rIh[X+We can use the default transaction manager:rJrK}rL(h_j@h jHubah_jWubh)rM}rN(h0}rO(U testnodetyperPXdoctestrQUgroupsrR]rShah2]rTh4]rUh;]rVhhh}rWh=]rXh?]rYuh hhLKh hhah9h X >>> for attempt in transaction.attempts(): ... with attempt as t: ... t.note('test') ... print("%s %s" % (dm['ntry'], ntry)) ... ntry += 1 ... dm['ntry'] = ntry ... if ntry % 3: ... raise Retry(ntry) 3 3 3 4 3 5rZhN]r[h[X >>> for attempt in transaction.attempts(): ... with attempt as t: ... t.note('test') ... print("%s %s" % (dm['ntry'], ntry)) ... ntry += 1 ... dm['ntry'] = ntry ... if ntry % 3: ... raise Retry(ntry) 3 3 3 4 3 5r\r]}r^(h_jMh Uubah_jWubh)r_}r`(h0}ra(h?]rbh;]rch2]rdh4]reh=]rfuh hhLKh hhah9h XSometimes, a data manager doesn't raise exceptions directly, but wraps other other systems that raise exceptions outside of it's control. Data managers can provide a should_retry method that takes an exception instance and returns True if the transaction should be attempted again.rghN]rhh[XSometimes, a data manager doesn't raise exceptions directly, but wraps other other systems that raise exceptions outside of it's control. Data managers can provide a should_retry method that takes an exception instance and returns True if the transaction should be attempted again.rirj}rk(h_j_h jgubah_jWubh)rl}rm(h0}rn(U testnodetyperoXdoctestrpUgroupsrq]rrhah2]rsh4]rth;]ruhhh}rvh=]rwh?]rxuh hhLKh hhah9h XJ>>> class DM(transaction.tests.savepointsample.SampleSavepointDataManager): ... def should_retry(self, e): ... if 'should retry' in str(e): ... return True >>> ntry = 0 >>> dm2 = DM() >>> with transaction.manager: ... dm2['ntry'] = 0 >>> for attempt in transaction.manager.attempts(): ... with attempt: ... print("%s %s" % (dm['ntry'], ntry)) ... ntry += 1 ... dm['ntry'] = ntry ... dm2['ntry'] = ntry ... if ntry % 3: ... raise ValueError('we really should retry this') 6 0 6 1 6 2 >>> dm2['ntry'] 3ryhN]rzh[XJ>>> class DM(transaction.tests.savepointsample.SampleSavepointDataManager): ... def should_retry(self, e): ... if 'should retry' in str(e): ... return True >>> ntry = 0 >>> dm2 = DM() >>> with transaction.manager: ... dm2['ntry'] = 0 >>> for attempt in transaction.manager.attempts(): ... with attempt: ... print("%s %s" % (dm['ntry'], ntry)) ... ntry += 1 ... dm['ntry'] = ntry ... dm2['ntry'] = ntry ... if ntry % 3: ... raise ValueError('we really should retry this') 6 0 6 1 6 2 >>> dm2['ntry'] 3r{r|}r}(h_jlh Uubah_jWubeh Uhah9h_hDubeh Uhah9h_hubhjWhhuU decorationr~NhN]rhDaU footnote_refsr}rU current_linerNUsettingsr(cdocutils.frontend Values ror}r(U sectnum_xformrKU halt_levelrKU datestamprNU tab_widthrKU toc_backlinksrUentryrUembed_stylesheetrUgettext_compactrU report_levelrKUstrip_commentsrNU source_linkrNUenvrNU smart_quotesrU_disable_configrNUsectsubtitle_xformrUinput_encoding_error_handlerrUstrictrU rfc_base_urlrUhttp://tools.ietf.org/html/rUfile_insertion_enabledrUdebugrNUwarning_streamrNU strip_classesrNUoutput_encodingrUutf-8rU docinfo_xformrKU source_urlrNUdoctitle_xformrUdump_internalsrNUfootnote_backlinksrKU id_prefixrUUpep_referencesrNhYNUauto_id_prefixrUidrUstrip_elements_with_classesrNUoutput_encoding_error_handlerrjU _config_filesr]rU pep_base_urlrUhttp://www.python.org/dev/peps/rU _destinationrNUerror_encodingrUUTF-8rU tracebackrUrfc_referencesrNUexpose_internalsrNUtrim_footnote_reference_spacerUrecord_dependenciesrNU_sourcerUA/home/tseaver/projects/Zope/ZODB/transaction/docs/convenience.rstrU raw_enabledrKUconfigrNU generatorrNUerror_encoding_error_handlerrUbackslashreplacerU language_coderUenrUinput_encodingrU utf-8-sigrUstrict_visitorrNUsyntax_highlightrUlongrUexit_status_levelrKU dump_settingsrNUcloak_email_addressesrUdump_transformsrNUdump_pseudo_xmlrNUpep_file_url_templaterUpep-%04drubUtransform_messagesr]rUautofootnote_startrKub.transaction-1.4.3/docs/_build/doctrees/hooks.doctree0000664000175000017500000007560012312641212022444 0ustar tseavertseavercdocutils.nodes document q)q}q(Urefnamesq}qUsubstitution_defsq}qUautofootnote_refsq]q Utagnameq Udocumentq U rawsourceq UU nametypesq }q(X!hooking the transaction machineryqNXthe addaftercommithook() methodqNX the addbeforecommithook() methodqNuUreporterqNUsymbol_footnote_startqKUid_startqKh hUnameidsq}q(hU!hooking-the-transaction-machineryqhUthe-addaftercommithook-methodqhUthe-addbeforecommithook-methodquUsymbol_footnote_refsq]qUindirect_targetsq]qUcurrent_sourceqNUparse_messagesq]q U autofootnotesq!]q"Urefidsq#}q$U transformerq%NUsymbol_footnotesq&]q'U footnotesq(]q)U citation_refsq*}q+Usubstitution_namesq,}q-U citationsq.]q/U attributesq0}q1(Uidsq2]q3Udupnamesq4]q5Usourceq6cdocutils.nodes reprunicode q7X;/home/tseaver/projects/Zope/ZODB/transaction/docs/hooks.rstq8q9}q:bUbackrefsq;]qUclassesq?]q@uUidsqA}qB(hcdocutils.nodes section qC)qD}qE(h0}qF(h?]qGh;]qHh2]qIhah4]qJh=]qKhauh hUlineqLKh UsectionqMUchildrenqN]qO(cdocutils.nodes title qP)qQ}qR(h0}qS(h?]qTh;]qUh2]qVh4]qWh=]qXuh hhLKh UtitleqYhN]qZcdocutils.nodes Text q[X!Hooking the Transaction Machineryq\q]}q^(Uparentq_hQh X!Hooking the Transaction Machineryq`ubah h`Usourceqah9h_hDubhC)qb}qc(h0}qd(h?]qeh;]qfh2]qghah4]qhh=]qihauh hhLKh hMhN]qj(hP)qk}ql(h0}qm(h?]qnh;]qoh2]qph4]qqh=]qruh hhLKh hYhN]qs(h[XThe qtqu}qv(h_hkh XThe qwubcsphinx.addnodes pending_xref qx)qy}qz(h0}q{(Urefdocq|Xhooksq}Urefwarnq~U py:moduleqNUpy:classqNh2]qh4]qU refexplicitqUreftypeqXmethqh;]qh=]qU refdomainqXpyqh?]qU reftargetqXaddBeforeCommitHookquhLKh U pending_xrefqhah9h X:meth:`addBeforeCommitHook`qhN]qcdocutils.nodes literal q)q}q(h_hyh UliteralqhN]qh[XaddBeforeCommitHook()qq}q(h_hh Uubah hh0}q(h?]q(UxrefqhXpy-methqeh;]qh2]qh4]qh=]quubah_hkubh[X Methodqq}q(h_hkh X Methodqubeh X&The :meth:`addBeforeCommitHook` Methodqhah9h_hbubcdocutils.nodes paragraph q)q}q(h0}q(h?]qh;]qh2]qh4]qh=]quh hhLKh U paragraphqhah9h XALet's define a hook to call, and a way to see that it was called.qhN]qh[XALet's define a hook to call, and a way to see that it was called.qq}q(h_hh hubah_hbubcdocutils.nodes literal_block q)q}q(h0}q(U testnodetypeqXdoctestqUgroupsq]qUdefaultqah2]qh4]qh;]qU xml:spaceqUpreserveqUoptionsq}qh=]qh?]quh hhLK h U literal_blockqhah9h X>>> log = [] >>> def reset_log(): ... del log[:] >>> def hook(arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... log.append("arg %r kw1 %r kw2 %r" % (arg, kw1, kw2))qhN]qh[X>>> log = [] >>> def reset_log(): ... del log[:] >>> def hook(arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... log.append("arg %r kw1 %r kw2 %r" % (arg, kw1, kw2))qɅq}q(h_hh Uubah_hbubh)q}q(h0}q(h?]qh;]qh2]qh4]qh=]quh hhLKh hhah9h X)Now register the hook with a transaction.qhN]qh[X)Now register the hook with a transaction.qօq}q(h_hh hubah_hbubh)q}q(h0}q(U testnodetypeqXdoctestqUgroupsq]qhah2]qh4]qh;]qhhh}qh=]qh?]quh hhLKh hhah9h X>>> from transaction import begin >>> from transaction._compat import func_name >>> import transaction >>> t = begin() >>> t.addBeforeCommitHook(hook, '1')qhN]qh[X>>> from transaction import begin >>> from transaction._compat import func_name >>> import transaction >>> t = begin() >>> t.addBeforeCommitHook(hook, '1')q腁q}q(h_hh Uubah_hbubh)q}q(h0}q(h?]qh;]qh2]qh4]qh=]quh hhLKh hhah9h X.We can see that the hook is indeed registered.qhN]qh[X.We can see that the hook is indeed registered.qq}q(h_hh hubah_hbubh)q}q(h0}q(U testnodetypeqXdoctestqUgroupsq]qhah2]qh4]rh;]rhhh}rh=]rh?]ruh hhLKh hhah9h Xn>>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getBeforeCommitHooks()] [('hook', ('1',), {})]rhN]rh[Xn>>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getBeforeCommitHooks()] [('hook', ('1',), {})]rr}r (h_hh Uubah_hbubh)r }r (h0}r (h?]r h;]rh2]rh4]rh=]ruh hhLK$h hhah9h XGWhen transaction commit starts, the hook is called, with its arguments.rhN]rh[XGWhen transaction commit starts, the hook is called, with its arguments.rr}r(h_j h jubah_hbubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]r hhh}r!h=]r"h?]r#uh hhLK'h hhah9h XW>>> log [] >>> t.commit() >>> log ["arg '1' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log()r$hN]r%h[XW>>> log [] >>> t.commit() >>> log ["arg '1' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log()r&r'}r((h_jh Uubah_hbubh)r)}r*(h0}r+(h?]r,h;]r-h2]r.h4]r/h=]r0uh hhLK0h hhah9h X{A hook's registration is consumed whenever the hook is called. Since the hook above was called, it's no longer registered:r1hN]r2h[X{A hook's registration is consumed whenever the hook is called. Since the hook above was called, it's no longer registered:r3r4}r5(h_j)h j1ubah_hbubh)r6}r7(h0}r8(U testnodetyper9Xdoctestr:Ugroupsr;]r<hah2]r=h4]r>h;]r?hhh}r@h=]rAh?]rBuh hhLK3h hhah9h Xd>>> from transaction import commit >>> len(list(t.getBeforeCommitHooks())) 0 >>> commit() >>> log []rChN]rDh[Xd>>> from transaction import commit >>> len(list(t.getBeforeCommitHooks())) 0 >>> commit() >>> log []rErF}rG(h_j6h Uubah_hbubh)rH}rI(h0}rJ(h?]rKh;]rLh2]rMh4]rNh=]rOuh hhLKh hhah9h X>>> t = begin() >>> t.addBeforeCommitHook(hook, 'A', dict(kw1='B')) >>> dummy = t.savepoint() >>> log [] >>> t.commit() >>> log ["arg 'A' kw1 'B' kw2 'no_kw2'"] >>> reset_log()rbhN]rch[X>>> t = begin() >>> t.addBeforeCommitHook(hook, 'A', dict(kw1='B')) >>> dummy = t.savepoint() >>> log [] >>> t.commit() >>> log ["arg 'A' kw1 'B' kw2 'no_kw2'"] >>> reset_log()rdre}rf(h_jUh Uubah_hbubh)rg}rh(h0}ri(h?]rjh;]rkh2]rlh4]rmh=]rnuh hhLKJh hhah9h X/If a transaction is aborted, no hook is called.rohN]rph[X/If a transaction is aborted, no hook is called.rqrr}rs(h_jgh joubah_hbubh)rt}ru(h0}rv(U testnodetyperwXdoctestrxUgroupsry]rzhah2]r{h4]r|h;]r}hhh}r~h=]rh?]ruh hhLKLh hhah9h X>>> from transaction import abort >>> t = begin() >>> t.addBeforeCommitHook(hook, ["OOPS!"]) >>> abort() >>> log [] >>> commit() >>> log []rhN]rh[X>>> from transaction import abort >>> t = begin() >>> t.addBeforeCommitHook(hook, ["OOPS!"]) >>> abort() >>> log [] >>> commit() >>> log []rr}r(h_jth Uubah_hbubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKXh hhah9h XThe hook is called before the commit does anything, so even if the commit fails the hook will have been called. To provoke failures in commit, we'll add failing resource manager to the transaction.rhN]rh[XThe hook is called before the commit does anything, so even if the commit fails the hook will have been called. To provoke failures in commit, we'll add failing resource manager to the transaction.rr}r(h_jh jubah_hbubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLK\h hhah9h X>>> class CommitFailure(Exception): ... pass >>> class FailingDataManager: ... def tpc_begin(self, txn, sub=False): ... raise CommitFailure('failed') ... def abort(self, txn): ... pass >>> t = begin() >>> t.join(FailingDataManager()) >>> t.addBeforeCommitHook(hook, '2') >>> from transaction.tests.common import DummyFile >>> from transaction.tests.common import Monkey >>> from transaction.tests.common import assertRaisesEx >>> from transaction import _transaction >>> buffer = DummyFile() >>> with Monkey(_transaction, _TB_BUFFER=buffer): ... err = assertRaisesEx(CommitFailure, t.commit) >>> log ["arg '2' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log()rhN]rh[X>>> class CommitFailure(Exception): ... pass >>> class FailingDataManager: ... def tpc_begin(self, txn, sub=False): ... raise CommitFailure('failed') ... def abort(self, txn): ... pass >>> t = begin() >>> t.join(FailingDataManager()) >>> t.addBeforeCommitHook(hook, '2') >>> from transaction.tests.common import DummyFile >>> from transaction.tests.common import Monkey >>> from transaction.tests.common import assertRaisesEx >>> from transaction import _transaction >>> buffer = DummyFile() >>> with Monkey(_transaction, _TB_BUFFER=buffer): ... err = assertRaisesEx(CommitFailure, t.commit) >>> log ["arg '2' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log()rr}r(h_jh Uubah_hbubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKvh hhah9h XLet's register several hooks.rhN]rh[XLet's register several hooks.rr}r(h_jh jubah_hbubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLKxh hhah9h X{>>> t = begin() >>> t.addBeforeCommitHook(hook, '4', dict(kw1='4.1')) >>> t.addBeforeCommitHook(hook, '5', dict(kw2='5.2'))rhN]rh[X{>>> t = begin() >>> t.addBeforeCommitHook(hook, '4', dict(kw1='4.1')) >>> t.addBeforeCommitHook(hook, '5', dict(kw2='5.2'))rr}r(h_jh Uubah_hbubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLK~h hhah9h X<They are returned in the same order by getBeforeCommitHooks.rhN]rh[X<They are returned in the same order by getBeforeCommitHooks.rr}r(h_jh jubah_hbubh)r}r(h0}r(UtestrX>>> [(func_name(hook), args, kws) #doctest: +NORMALIZE_WHITESPACE ... for hook, args, kws in t.getBeforeCommitHooks()] [('hook', ('4',), {'kw1': '4.1'}), ('hook', ('5',), {'kw2': '5.2'})]rU testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLKh hhah9h X>>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getBeforeCommitHooks()] [('hook', ('4',), {'kw1': '4.1'}), ('hook', ('5',), {'kw2': '5.2'})]rhN]rh[X>>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getBeforeCommitHooks()] [('hook', ('4',), {'kw1': '4.1'}), ('hook', ('5',), {'kw2': '5.2'})]rr}r(h_jh Uubah_hbubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKh hhah9h X)And commit also calls them in this order.rhN]rh[X)And commit also calls them in this order.rr}r(h_jh jubah_hbubh)r}r(h0}r(jX>>> t.commit() >>> len(log) 2 >>> log #doctest: +NORMALIZE_WHITESPACE ["arg '4' kw1 '4.1' kw2 'no_kw2'", "arg '5' kw1 'no_kw1' kw2 '5.2'"] >>> reset_log()rU testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLKh hhah9h X|>>> t.commit() >>> len(log) 2 >>> log ["arg '4' kw1 '4.1' kw2 'no_kw2'", "arg '5' kw1 'no_kw1' kw2 '5.2'"] >>> reset_log()rhN]rh[X|>>> t.commit() >>> len(log) 2 >>> log ["arg '4' kw1 '4.1' kw2 'no_kw2'", "arg '5' kw1 'no_kw1' kw2 '5.2'"] >>> reset_log()rr}r(h_jh Uubah_hbubh)r}r(h0}r(h?]rh;]r h2]r h4]r h=]r uh hhLKh hhah9h XmWhile executing, a hook can itself add more hooks, and they will all be called before the real commit starts.r hN]rh[XmWhile executing, a hook can itself add more hooks, and they will all be called before the real commit starts.rr}r(h_jh j ubah_hbubh)r}r(h0}r(jX>>> def recurse(txn, arg): ... log.append('rec' + str(arg)) ... if arg: ... txn.addBeforeCommitHook(hook, '-') ... txn.addBeforeCommitHook(recurse, (txn, arg-1)) >>> t = begin() >>> t.addBeforeCommitHook(recurse, (t, 3)) >>> commit() >>> log #doctest: +NORMALIZE_WHITESPACE ['rec3', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec2', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec1', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec0'] >>> reset_log()rU testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLKh hhah9h X>>> def recurse(txn, arg): ... log.append('rec' + str(arg)) ... if arg: ... txn.addBeforeCommitHook(hook, '-') ... txn.addBeforeCommitHook(recurse, (txn, arg-1)) >>> t = begin() >>> t.addBeforeCommitHook(recurse, (t, 3)) >>> commit() >>> log ['rec3', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec2', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec1', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec0'] >>> reset_log()r hN]r!h[X>>> def recurse(txn, arg): ... log.append('rec' + str(arg)) ... if arg: ... txn.addBeforeCommitHook(hook, '-') ... txn.addBeforeCommitHook(recurse, (txn, arg-1)) >>> t = begin() >>> t.addBeforeCommitHook(recurse, (t, 3)) >>> commit() >>> log ['rec3', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec2', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec1', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec0'] >>> reset_log()r"r#}r$(h_jh Uubah_hbubeh Uhah9h_hDubhC)r%}r&(h0}r'(h?]r(h;]r)h2]r*hah4]r+h=]r,hauh hhLKh hMhN]r-(hP)r.}r/(h0}r0(h?]r1h;]r2h2]r3h4]r4h=]r5uh hhLKh hYhN]r6(h[XThe r7r8}r9(h_j.h XThe r:ubhx)r;}r<(h0}r=(h|h}h~hNhNh2]r>h4]r?U refexplicitr@UreftyperAXmethrBh;]rCh=]rDU refdomainrEXpyrFh?]rGhXaddAfterCommitHookrHuhLKh hhah9h X:meth:`addAfterCommitHook`rIhN]rJh)rK}rL(h_j;h hhN]rMh[XaddAfterCommitHook()rNrO}rP(h_jKh Uubah jIh0}rQ(h?]rR(hjFXpy-methrSeh;]rTh2]rUh4]rVh=]rWuubah_j.ubh[X MethodrXrY}rZ(h_j.h X Methodr[ubeh X%The :meth:`addAfterCommitHook` Methodr\hah9h_j%ubh)r]}r^(h0}r_(h?]r`h;]rah2]rbh4]rch=]rduh hhLKh hhah9h XALet's define a hook to call, and a way to see that it was called.rehN]rfh[XALet's define a hook to call, and a way to see that it was called.rgrh}ri(h_j]h jeubah_j%ubh)rj}rk(h0}rl(U testnodetypermXdoctestrnUgroupsro]rphah2]rqh4]rrh;]rshhh}rth=]ruh?]rvuh hhLKh hhah9h X>>> log = [] >>> def reset_log(): ... del log[:] >>> def hook(status, arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... log.append("%r arg %r kw1 %r kw2 %r" % (status, arg, kw1, kw2))rwhN]rxh[X>>> log = [] >>> def reset_log(): ... del log[:] >>> def hook(status, arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... log.append("%r arg %r kw1 %r kw2 %r" % (status, arg, kw1, kw2))ryrz}r{(h_jjh Uubah_j%ubh)r|}r}(h0}r~(h?]rh;]rh2]rh4]rh=]ruh hhLKh hhah9h X)Now register the hook with a transaction.rhN]rh[X)Now register the hook with a transaction.rr}r(h_j|h jubah_j%ubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLKh hhah9h X>>> from transaction import begin >>> from transaction._compat import func_name >>> t = begin() >>> t.addAfterCommitHook(hook, '1')rhN]rh[X>>> from transaction import begin >>> from transaction._compat import func_name >>> t = begin() >>> t.addAfterCommitHook(hook, '1')rr}r(h_jh Uubah_j%ubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKh hhah9h X.We can see that the hook is indeed registered.rhN]rh[X.We can see that the hook is indeed registered.rr}r(h_jh jubah_j%ubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLKh hhah9h Xm>>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getAfterCommitHooks()] [('hook', ('1',), {})]rhN]rh[Xm>>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getAfterCommitHooks()] [('hook', ('1',), {})]rr}r(h_jh Uubah_j%ubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKh hhah9h XHWhen transaction commit is done, the hook is called, with its arguments.rhN]rh[XHWhen transaction commit is done, the hook is called, with its arguments.rr}r(h_jh jubah_j%ubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLKh hhah9h X\>>> log [] >>> t.commit() >>> log ["True arg '1' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log()rhN]rh[X\>>> log [] >>> t.commit() >>> log ["True arg '1' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log()rr}r(h_jh Uubah_j%ubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKh hhah9h X{A hook's registration is consumed whenever the hook is called. Since the hook above was called, it's no longer registered:rhN]rh[X{A hook's registration is consumed whenever the hook is called. Since the hook above was called, it's no longer registered:rr}r(h_jh jubah_j%ubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLKh hhah9h Xc>>> from transaction import commit >>> len(list(t.getAfterCommitHooks())) 0 >>> commit() >>> log []rhN]rh[Xc>>> from transaction import commit >>> len(list(t.getAfterCommitHooks())) 0 >>> commit() >>> log []rr}r(h_jh Uubah_j%ubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKh hhah9h XAThe hook is only called after a full commit, not for a savepoint.rhN]rh[XAThe hook is only called after a full commit, not for a savepoint.rr}r(h_jh jubah_j%ubh)r}r(h0}r(U testnodetyperXdoctestr Ugroupsr ]r hah2]r h4]r h;]rhhh}rh=]rh?]ruh hhLKh hhah9h X>>> t = begin() >>> t.addAfterCommitHook(hook, 'A', dict(kw1='B')) >>> dummy = t.savepoint() >>> log [] >>> t.commit() >>> log ["True arg 'A' kw1 'B' kw2 'no_kw2'"] >>> reset_log()rhN]rh[X>>> t = begin() >>> t.addAfterCommitHook(hook, 'A', dict(kw1='B')) >>> dummy = t.savepoint() >>> log [] >>> t.commit() >>> log ["True arg 'A' kw1 'B' kw2 'no_kw2'"] >>> reset_log()rr}r(h_jh Uubah_j%ubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLKh hhah9h X/If a transaction is aborted, no hook is called.rhN]r h[X/If a transaction is aborted, no hook is called.r!r"}r#(h_jh jubah_j%ubh)r$}r%(h0}r&(U testnodetyper'Xdoctestr(Ugroupsr)]r*hah2]r+h4]r,h;]r-hhh}r.h=]r/h?]r0uh hhLKh hhah9h X>>> from transaction import abort >>> t = begin() >>> t.addAfterCommitHook(hook, ["OOPS!"]) >>> abort() >>> log [] >>> commit() >>> log []r1hN]r2h[X>>> from transaction import abort >>> t = begin() >>> t.addAfterCommitHook(hook, ["OOPS!"]) >>> abort() >>> log [] >>> commit() >>> log []r3r4}r5(h_j$h Uubah_j%ubh)r6}r7(h0}r8(h?]r9h;]r:h2]r;h4]r<h=]r=uh hhLKh hhah9h XThe hook is called after the commit is done, so even if the commit fails the hook will have been called. To provoke failures in commit, we'll add failing resource manager to the transaction.r>hN]r?h[XThe hook is called after the commit is done, so even if the commit fails the hook will have been called. To provoke failures in commit, we'll add failing resource manager to the transaction.r@rA}rB(h_j6h j>ubah_j%ubh)rC}rD(h0}rE(U testnodetyperFXdoctestrGUgroupsrH]rIhah2]rJh4]rKh;]rLhhh}rMh=]rNh?]rOuh hhLMh hhah9h X>>> class CommitFailure(Exception): ... pass >>> class FailingDataManager: ... def tpc_begin(self, txn): ... raise CommitFailure('failed') ... def abort(self, txn): ... pass >>> t = begin() >>> t.join(FailingDataManager()) >>> t.addAfterCommitHook(hook, '2') >>> from transaction.tests.common import DummyFile >>> from transaction.tests.common import Monkey >>> from transaction.tests.common import assertRaisesEx >>> from transaction import _transaction >>> buffer = DummyFile() >>> with Monkey(_transaction, _TB_BUFFER=buffer): ... err = assertRaisesEx(CommitFailure, t.commit) >>> log ["False arg '2' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log()rPhN]rQh[X>>> class CommitFailure(Exception): ... pass >>> class FailingDataManager: ... def tpc_begin(self, txn): ... raise CommitFailure('failed') ... def abort(self, txn): ... pass >>> t = begin() >>> t.join(FailingDataManager()) >>> t.addAfterCommitHook(hook, '2') >>> from transaction.tests.common import DummyFile >>> from transaction.tests.common import Monkey >>> from transaction.tests.common import assertRaisesEx >>> from transaction import _transaction >>> buffer = DummyFile() >>> with Monkey(_transaction, _TB_BUFFER=buffer): ... err = assertRaisesEx(CommitFailure, t.commit) >>> log ["False arg '2' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log()rRrS}rT(h_jCh Uubah_j%ubh)rU}rV(h0}rW(h?]rXh;]rYh2]rZh4]r[h=]r\uh hhLMh hhah9h XLet's register several hooks.r]hN]r^h[XLet's register several hooks.r_r`}ra(h_jUh j]ubah_j%ubh)rb}rc(h0}rd(U testnodetypereXdoctestrfUgroupsrg]rhhah2]rih4]rjh;]rkhhh}rlh=]rmh?]rnuh hhLMh hhah9h Xy>>> t = begin() >>> t.addAfterCommitHook(hook, '4', dict(kw1='4.1')) >>> t.addAfterCommitHook(hook, '5', dict(kw2='5.2'))rohN]rph[Xy>>> t = begin() >>> t.addAfterCommitHook(hook, '4', dict(kw1='4.1')) >>> t.addAfterCommitHook(hook, '5', dict(kw2='5.2'))rqrr}rs(h_jbh Uubah_j%ubh)rt}ru(h0}rv(h?]rwh;]rxh2]ryh4]rzh=]r{uh hhLM$h hhah9h X;They are returned in the same order by getAfterCommitHooks.r|hN]r}h[X;They are returned in the same order by getAfterCommitHooks.r~r}r(h_jth j|ubah_j%ubh)r}r(h0}r(jX>>> [(func_name(hook), args, kws) #doctest: +NORMALIZE_WHITESPACE ... for hook, args, kws in t.getAfterCommitHooks()] [('hook', ('4',), {'kw1': '4.1'}), ('hook', ('5',), {'kw2': '5.2'})]rU testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLM&h hhah9h X>>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getAfterCommitHooks()] [('hook', ('4',), {'kw1': '4.1'}), ('hook', ('5',), {'kw2': '5.2'})]rhN]rh[X>>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getAfterCommitHooks()] [('hook', ('4',), {'kw1': '4.1'}), ('hook', ('5',), {'kw2': '5.2'})]rr}r(h_jh Uubah_j%ubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLM-h hhah9h X)And commit also calls them in this order.rhN]rh[X)And commit also calls them in this order.rr}r(h_jh jubah_j%ubh)r}r(h0}r(jX>>> t.commit() >>> len(log) 2 >>> log #doctest: +NORMALIZE_WHITESPACE ["True arg '4' kw1 '4.1' kw2 'no_kw2'", "True arg '5' kw1 'no_kw1' kw2 '5.2'"] >>> reset_log()rU testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLM/h hhah9h X>>> t.commit() >>> len(log) 2 >>> log ["True arg '4' kw1 '4.1' kw2 'no_kw2'", "True arg '5' kw1 'no_kw1' kw2 '5.2'"] >>> reset_log()rhN]rh[X>>> t.commit() >>> len(log) 2 >>> log ["True arg '4' kw1 '4.1' kw2 'no_kw2'", "True arg '5' kw1 'no_kw1' kw2 '5.2'"] >>> reset_log()rr}r(h_jh Uubah_j%ubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLM9h hhah9h XmWhile executing, a hook can itself add more hooks, and they will all be called before the real commit starts.rhN]rh[XmWhile executing, a hook can itself add more hooks, and they will all be called before the real commit starts.rr}r(h_jh jubah_j%ubh)r}r(h0}r(jX>>> def recurse(status, txn, arg): ... log.append('rec' + str(arg)) ... if arg: ... txn.addAfterCommitHook(hook, '-') ... txn.addAfterCommitHook(recurse, (txn, arg-1)) >>> t = begin() >>> t.addAfterCommitHook(recurse, (t, 3)) >>> commit() >>> log #doctest: +NORMALIZE_WHITESPACE ['rec3', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec2', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec1', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec0'] >>> reset_log()rU testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLM<h hhah9h X>>> def recurse(status, txn, arg): ... log.append('rec' + str(arg)) ... if arg: ... txn.addAfterCommitHook(hook, '-') ... txn.addAfterCommitHook(recurse, (txn, arg-1)) >>> t = begin() >>> t.addAfterCommitHook(recurse, (t, 3)) >>> commit() >>> log ['rec3', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec2', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec1', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec0'] >>> reset_log()rhN]rh[X>>> def recurse(status, txn, arg): ... log.append('rec' + str(arg)) ... if arg: ... txn.addAfterCommitHook(hook, '-') ... txn.addAfterCommitHook(recurse, (txn, arg-1)) >>> t = begin() >>> t.addAfterCommitHook(recurse, (t, 3)) >>> commit() >>> log ['rec3', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec2', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec1', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec0'] >>> reset_log()rr}r(h_jh Uubah_j%ubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLMQh hhah9h XIf an after commit hook is raising an exception then it will log a message at error level so that if other hooks are registered they can be executed. We don't support execution dependencies at this level.rhN]rh[XIf an after commit hook is raising an exception then it will log a message at error level so that if other hooks are registered they can be executed. We don't support execution dependencies at this level.rr}r(h_jh jubah_j%ubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]rhhh}rh=]rh?]ruh hhLMUh hhah9h X >>> from transaction import TransactionManager >>> from transaction.tests.test__manager import DataObject >>> mgr = TransactionManager() >>> do = DataObject(mgr) >>> def hookRaise(status, arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... raise TypeError("Fake raise") >>> t = begin() >>> t.addAfterCommitHook(hook, ('-', 1)) >>> t.addAfterCommitHook(hookRaise, ('-', 2)) >>> t.addAfterCommitHook(hook, ('-', 3)) >>> commit() >>> log ["True arg '-' kw1 1 kw2 'no_kw2'", "True arg '-' kw1 3 kw2 'no_kw2'"] >>> reset_log()rhN]rh[X >>> from transaction import TransactionManager >>> from transaction.tests.test__manager import DataObject >>> mgr = TransactionManager() >>> do = DataObject(mgr) >>> def hookRaise(status, arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... raise TypeError("Fake raise") >>> t = begin() >>> t.addAfterCommitHook(hook, ('-', 1)) >>> t.addAfterCommitHook(hookRaise, ('-', 2)) >>> t.addAfterCommitHook(hook, ('-', 3)) >>> commit() >>> log ["True arg '-' kw1 1 kw2 'no_kw2'", "True arg '-' kw1 3 kw2 'no_kw2'"] >>> reset_log()rr}r(h_jh Uubah_j%ubh)r}r(h0}r(h?]rh;]rh2]rh4]rh=]ruh hhLMkh hhah9h XdTest that the associated transaction manager has been cleanup when after commit hooks are registeredrhN]rh[XdTest that the associated transaction manager has been cleanup when after commit hooks are registeredrr}r(h_jh jubah_j%ubh)r}r(h0}r(U testnodetyperXdoctestrUgroupsr]rhah2]rh4]rh;]r hhh}r h=]r h?]r uh hhLMnh hhah9h X >>> mgr = TransactionManager() >>> do = DataObject(mgr) >>> t = begin() >>> t._manager._txn is not None True >>> t.addAfterCommitHook(hook, ('-', 1)) >>> commit() >>> log ["True arg '-' kw1 1 kw2 'no_kw2'"] >>> t._manager._txn is not None False >>> reset_log()r hN]rh[X >>> mgr = TransactionManager() >>> do = DataObject(mgr) >>> t = begin() >>> t._manager._txn is not None True >>> t.addAfterCommitHook(hook, ('-', 1)) >>> commit() >>> log ["True arg '-' kw1 1 kw2 'no_kw2'"] >>> t._manager._txn is not None False >>> reset_log()rr}r(h_jh Uubah_j%ubeh Uhah9h_hDubeh Uhah9h_hubhhbhj%uU decorationrNhN]rhDaU footnote_refsr}rU current_linerNUsettingsr(cdocutils.frontend Values ror}r(U sectnum_xformrKU halt_levelrKU datestamprNU tab_widthrKU toc_backlinksrUentryr Uembed_stylesheetr!Ugettext_compactr"U report_levelr#KUstrip_commentsr$NU source_linkr%NUenvr&NU smart_quotesr'U_disable_configr(NUsectsubtitle_xformr)Uinput_encoding_error_handlerr*Ustrictr+U rfc_base_urlr,Uhttp://tools.ietf.org/html/r-Ufile_insertion_enabledr.Udebugr/NUwarning_streamr0NU strip_classesr1NUoutput_encodingr2Uutf-8r3U docinfo_xformr4KU source_urlr5NUdoctitle_xformr6Udump_internalsr7NUfootnote_backlinksr8KU id_prefixr9UUpep_referencesr:NhYNUauto_id_prefixr;Uidr<Ustrip_elements_with_classesr=NUoutput_encoding_error_handlerr>j+U _config_filesr?]r@U pep_base_urlrAUhttp://www.python.org/dev/peps/rBU _destinationrCNUerror_encodingrDUUTF-8rEU tracebackrFUrfc_referencesrGNUexpose_internalsrHNUtrim_footnote_reference_spacerIUrecord_dependenciesrJNU_sourcerKU;/home/tseaver/projects/Zope/ZODB/transaction/docs/hooks.rstrLU raw_enabledrMKUconfigrNNU generatorrONUerror_encoding_error_handlerrPUbackslashreplacerQU language_coderRUenrSUinput_encodingrTU utf-8-sigrUUstrict_visitorrVNUsyntax_highlightrWUlongrXUexit_status_levelrYKU dump_settingsrZNUcloak_email_addressesr[Udump_transformsr\NUdump_pseudo_xmlr]NUpep_file_url_templater^Upep-%04dr_ubUtransform_messagesr`]raUautofootnote_startrbKub.transaction-1.4.3/docs/_build/doctrees/savepoint.doctree0000664000175000017500000007023612312641212023331 0ustar tseavertseavercdocutils.nodes document q)q}q(Urefnamesq}qUsubstitution_defsq}qUautofootnote_refsq]q Utagnameq Udocumentq U rawsourceq UU nametypesq }q(X applicationsqNXsavepoint invalidationqNX savepointsqNXfailuresqNX#databases without savepoint supportqNuUreporterqNUsymbol_footnote_startqKUid_startqKh hUnameidsq}q(hU applicationsqhUsavepoint-invalidationqhU savepointsqhUfailuresqhU#databases-without-savepoint-supportquUsymbol_footnote_refsq]qUindirect_targetsq ]q!Ucurrent_sourceq"NUparse_messagesq#]q$U autofootnotesq%]q&Urefidsq'}q(U transformerq)NUsymbol_footnotesq*]q+U footnotesq,]q-U citation_refsq.}q/Usubstitution_namesq0}q1U citationsq2]q3U attributesq4}q5(Uidsq6]q7Udupnamesq8]q9Usourceq:cdocutils.nodes reprunicode q;X?/home/tseaver/projects/Zope/ZODB/transaction/docs/savepoint.rstqbUbackrefsq?]q@UnamesqA]qBUclassesqC]qDuUidsqE}qF(hcdocutils.nodes section qG)qH}qI(h4}qJ(hC]qKh?]qLh6]qMhah8]qNhA]qOhauh hUlineqPKh UsectionqQUchildrenqR]qS(cdocutils.nodes title qT)qU}qV(h4}qW(hC]qXh?]qYh6]qZh8]q[hA]q\uh hhPKh Utitleq]hR]q^cdocutils.nodes Text q_X Applicationsq`qa}qb(UparentqchUh X Applicationsqdubah hdUsourceqeh=hchHubcdocutils.nodes paragraph qf)qg}qh(h4}qi(hC]qjh?]qkh6]qlh8]qmhA]qnuh hhPKh U paragraphqoheh=h XTo demonstrate how savepoints work with transactions, we've provided a sample data manager implementation that provides savepoint support. The primary purpose of this data manager is to provide code that can be read to understand how savepoints work. The secondary purpose is to provide support for demonstrating the correct operation of savepoint support within the transaction system. This data manager is very simple. It provides flat storage of named immutable values, like strings and numbers.qphR]qqh_XTo demonstrate how savepoints work with transactions, we've provided a sample data manager implementation that provides savepoint support. The primary purpose of this data manager is to provide code that can be read to understand how savepoints work. The secondary purpose is to provide support for demonstrating the correct operation of savepoint support within the transaction system. This data manager is very simple. It provides flat storage of named immutable values, like strings and numbers.qrqs}qt(hchgh hpubahchHubcdocutils.nodes literal_block qu)qv}qw(h4}qx(U testnodetypeqyXdoctestqzUgroupsq{]q|Udefaultq}ah6]q~h8]qh?]qU xml:spaceqUpreserveqUoptionsq}qhA]qhC]quh hhPKh U literal_blockqheh=h X>>> import transaction >>> from transaction.tests import savepointsample >>> dm = savepointsample.SampleSavepointDataManager() >>> dm['name'] = 'bob'qhR]qh_X>>> import transaction >>> from transaction.tests import savepointsample >>> dm = savepointsample.SampleSavepointDataManager() >>> dm['name'] = 'bob'qq}q(hchvh UubahchHubhf)q}q(h4}q(hC]qh?]qh6]qh8]qhA]quh hhPK"h hoheh=h X3As with other data managers, we can commit changes:qhR]qh_X3As with other data managers, we can commit changes:qq}q(hchh hubahchHubhu)q}q(h4}q(U testnodetypeqXdoctestqUgroupsq]qh}ah6]qh8]qh?]qhhh}qhA]qhC]quh hhPK$h hheh=h X->>> transaction.commit() >>> dm['name'] 'bob'qhR]qh_X->>> transaction.commit() >>> dm['name'] 'bob'qq}q(hchh UubahchHubhf)q}q(h4}q(hC]qh?]qh6]qh8]qhA]quh hhPK*h hoheh=h Xand abort changes:qhR]qh_Xand abort changes:qq}q(hchh hubahchHubhu)q}q(h4}q(U testnodetypeqXdoctestqUgroupsq]qh}ah6]qh8]qh?]qhhh}qhA]qhC]quh hhPK,h hheh=h X\>>> dm['name'] = 'sally' >>> dm['name'] 'sally' >>> transaction.abort() >>> dm['name'] 'bob'qhR]qh_X\>>> dm['name'] = 'sally' >>> dm['name'] 'sally' >>> transaction.abort() >>> dm['name'] 'bob'qȅq}q(hchh UubahchHubhf)q}q(h4}q(hC]qh?]qh6]qh8]qhA]quh hhPK5h hoheh=h XNow, let's look at an application that manages funds for people. It allows deposits and debits to be entered for multiple people. It accepts a sequence of entries and generates a sequence of status messages. For each entry, it applies the change and then validates the user's account. If the user's account is invalid, we roll back the change for that entry. The success or failure of an entry is indicated in the output status. First we'll initialize some accounts:qhR]qh_XNow, let's look at an application that manages funds for people. It allows deposits and debits to be entered for multiple people. It accepts a sequence of entries and generates a sequence of status messages. For each entry, it applies the change and then validates the user's account. If the user's account is invalid, we roll back the change for that entry. The success or failure of an entry is indicated in the output status. First we'll initialize some accounts:qՅq}q(hchh hubahchHubhu)q}q(h4}q(U testnodetypeqXdoctestqUgroupsq]qh}ah6]qh8]qh?]qhhh}qhA]qhC]quh hhPK=h hheh=h X>>> dm['bob-balance'] = 0.0 >>> dm['bob-credit'] = 0.0 >>> dm['sally-balance'] = 0.0 >>> dm['sally-credit'] = 100.0 >>> transaction.commit()qhR]qh_X>>> dm['bob-balance'] = 0.0 >>> dm['bob-credit'] = 0.0 >>> dm['sally-balance'] = 0.0 >>> dm['sally-credit'] = 100.0 >>> transaction.commit()q煁q}q(hchh UubahchHubhf)q}q(h4}q(hC]qh?]qh6]qh8]qhA]quh hhPKEh hoheh=h X?Now, we'll define a validation function to validate an account:qhR]qh_X?Now, we'll define a validation function to validate an account:qq}q(hchh hubahchHubhu)q}q(h4}q(U testnodetypeqXdoctestqUgroupsq]qh}ah6]qh8]qh?]rhhh}rhA]rhC]ruh hhPKGh hheh=h X>>> def validate_account(name): ... if dm[name+'-balance'] + dm[name+'-credit'] < 0: ... raise ValueError('Overdrawn', name)rhR]rh_X>>> def validate_account(name): ... if dm[name+'-balance'] + dm[name+'-credit'] < 0: ... raise ValueError('Overdrawn', name)rr}r(hchh UubahchHubhf)r }r (h4}r (hC]r h?]r h6]rh8]rhA]ruh hhPKMh hoheh=h XAnd a function to apply entries. If the function fails in some unexpected way, it rolls back all of its changes and prints the error:rhR]rh_XAnd a function to apply entries. If the function fails in some unexpected way, it rolls back all of its changes and prints the error:rr}r(hcj h jubahchHubhu)r}r(h4}r(U testnodetyperXdoctestrUgroupsr]rh}ah6]rh8]rh?]rhhh}r hA]r!hC]r"uh hhPKPh hheh=h X>>> def apply_entries(entries): ... savepoint = transaction.savepoint() ... try: ... for name, amount in entries: ... entry_savepoint = transaction.savepoint() ... try: ... dm[name+'-balance'] += amount ... validate_account(name) ... except ValueError as error: ... entry_savepoint.rollback() ... print("%s %s" % ('Error', str(error))) ... else: ... print("%s %s" % ('Updated', name)) ... except Exception as error: ... savepoint.rollback() ... print("%s" % ('Unexpected exception'))r#hR]r$h_X>>> def apply_entries(entries): ... savepoint = transaction.savepoint() ... try: ... for name, amount in entries: ... entry_savepoint = transaction.savepoint() ... try: ... dm[name+'-balance'] += amount ... validate_account(name) ... except ValueError as error: ... entry_savepoint.rollback() ... print("%s %s" % ('Error', str(error))) ... else: ... print("%s %s" % ('Updated', name)) ... except Exception as error: ... savepoint.rollback() ... print("%s" % ('Unexpected exception'))r%r&}r'(hcjh UubahchHubhf)r(}r)(h4}r*(hC]r+h?]r,h6]r-h8]r.hA]r/uh hhPKch hoheh=h X$Now let's try applying some entries:r0hR]r1h_X$Now let's try applying some entries:r2r3}r4(hcj(h j0ubahchHubhu)r5}r6(h4}r7(U testnodetyper8Xdoctestr9Ugroupsr:]r;h}ah6]r<h8]r=h?]r>hhh}r?hA]r@hC]rAuh hhPKeh hheh=h XP>>> apply_entries([ ... ('bob', 10.0), ... ('sally', 10.0), ... ('bob', 20.0), ... ('sally', 10.0), ... ('bob', -100.0), ... ('sally', -100.0), ... ]) Updated bob Updated sally Updated bob Updated sally Error ('Overdrawn', 'bob') Updated sally >>> dm['bob-balance'] 30.0 >>> dm['sally-balance'] -80.0rBhR]rCh_XP>>> apply_entries([ ... ('bob', 10.0), ... ('sally', 10.0), ... ('bob', 20.0), ... ('sally', 10.0), ... ('bob', -100.0), ... ('sally', -100.0), ... ]) Updated bob Updated sally Updated bob Updated sally Error ('Overdrawn', 'bob') Updated sally >>> dm['bob-balance'] 30.0 >>> dm['sally-balance'] -80.0rDrE}rF(hcj5h UubahchHubhf)rG}rH(h4}rI(hC]rJh?]rKh6]rLh8]rMhA]rNuh hhPK|h hoheh=h X5If we provide entries that cause an unexpected error:rOhR]rPh_X5If we provide entries that cause an unexpected error:rQrR}rS(hcjGh jOubahchHubhu)rT}rU(h4}rV(U testnodetyperWXdoctestrXUgroupsrY]rZh}ah6]r[h8]r\h?]r]hhh}r^hA]r_hC]r`uh hhPK~h hheh=h X>>> apply_entries([ ... ('bob', 10.0), ... ('sally', 10.0), ... ('bob', '20.0'), ... ('sally', 10.0), ... ]) Updated bob Updated sally Unexpected exceptionrahR]rbh_X>>> apply_entries([ ... ('bob', 10.0), ... ('sally', 10.0), ... ('bob', '20.0'), ... ('sally', 10.0), ... ]) Updated bob Updated sally Unexpected exceptionrcrd}re(hcjTh UubahchHubhf)rf}rg(h4}rh(hC]rih?]rjh6]rkh8]rlhA]rmuh hhPKh hoheh=h XBecause the apply_entries used a savepoint for the entire function, it was able to rollback the partial changes without rolling back changes made in the previous call to ``apply_entries``:rnhR]ro(h_XBecause the apply_entries used a savepoint for the entire function, it was able to rollback the partial changes without rolling back changes made in the previous call to rprq}rr(hcjfh XBecause the apply_entries used a savepoint for the entire function, it was able to rollback the partial changes without rolling back changes made in the previous call to rsubcdocutils.nodes literal rt)ru}rv(hcjfh UliteralrwhR]rxh_X apply_entriesryrz}r{(hcjuh Uubah X``apply_entries``r|h4}r}(hC]r~h?]rh6]rh8]rhA]ruubh_X:r}r(hcjfh X:ubehchHubhu)r}r(h4}r(U testnodetyperXdoctestrUgroupsr]rh}ah6]rh8]rh?]rhhh}rhA]rhC]ruh hhPKh hheh=h X9>>> dm['bob-balance'] 30.0 >>> dm['sally-balance'] -80.0rhR]rh_X9>>> dm['bob-balance'] 30.0 >>> dm['sally-balance'] -80.0rr}r(hcjh UubahchHubhf)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPKh hoheh=h XIIf we now abort the outer transactions, the earlier changes will go away:rhR]rh_XIIf we now abort the outer transactions, the earlier changes will go away:rr}r(hcjh jubahchHubhu)r}r(h4}r(U testnodetyperXdoctestrUgroupsr]rh}ah6]rh8]rh?]rhhh}rhA]rhC]ruh hhPKh hheh=h XO>>> transaction.abort() >>> dm['bob-balance'] 0.0 >>> dm['sally-balance'] 0.0rhR]rh_XO>>> transaction.abort() >>> dm['bob-balance'] 0.0 >>> dm['sally-balance'] 0.0rr}r(hcjh UubahchHubeh Uheh=hchG)r}r(h4}r(hC]rh?]rh6]rhah8]rhA]rhauh hhPKh hQhR]r(hT)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPKh h]hR]rh_X Savepointsrr}r(hcjh X Savepointsrubah jheh=hcjubhf)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPKh hoheh=h X^Savepoints provide a way to save to disk intermediate work done during a transaction allowing:rhR]rh_X^Savepoints provide a way to save to disk intermediate work done during a transaction allowing:rr}r(hcjh jubahcjubcdocutils.nodes bullet_list r)r}r(h4}r(UbulletrX-h6]rh8]rh?]rhA]rhC]ruh hhPKh U bullet_listrhR]r(cdocutils.nodes list_item r)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPNh U list_itemrhR]rhf)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruhPKh hoheh=h X5partial transaction (subtransaction) rollback (abort)rhR]rh_X5partial transaction (subtransaction) rollback (abort)rr}r(hcjh jubahcjubah X6partial transaction (subtransaction) rollback (abort) rheh=hcjubj)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPNh jhR]rhf)r}r(h4}r (hC]r h?]r h6]r h8]r hA]ruhPK h hoheh=h XIstate of saved objects to be freed, freeing on-line memory for other usesrhR]rh_XIstate of saved objects to be freed, freeing on-line memory for other usesrr}r(hcjh jubahcjubah XJstate of saved objects to be freed, freeing on-line memory for other uses rheh=hcjubeh Uheh=hcjubhf)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPK h hoheh=h XjSavepoints make it possible to write atomic subroutines that don't make top-level transaction commitments.rhR]rh_XjSavepoints make it possible to write atomic subroutines that don't make top-level transaction commitments.rr }r!(hcjh jubahcjubhHhG)r"}r#(h4}r$(hC]r%h?]r&h6]r'hah8]r(hA]r)hauh hhPKh hQhR]r*(hT)r+}r,(h4}r-(hC]r.h?]r/h6]r0h8]r1hA]r2uh hhPKh h]hR]r3h_XSavepoint invalidationr4r5}r6(hcj+h XSavepoint invalidationr7ubah j7heh=hcj"ubhf)r8}r9(h4}r:(hC]r;h?]r<h6]r=h8]r>hA]r?uh hhPKh hoheh=h X,A savepoint can be used any number of times:r@hR]rAh_X,A savepoint can be used any number of times:rBrC}rD(hcj8h j@ubahcj"ubhu)rE}rF(h4}rG(U testnodetyperHXdoctestrIUgroupsrJ]rKh}ah6]rLh8]rMh?]rNhhh}rOhA]rPhC]rQuh hhPKh hheh=h X>>> dm['bob-balance'] = 100.0 >>> dm['bob-balance'] 100.0 >>> savepoint = transaction.savepoint() >>> dm['bob-balance'] = 200.0 >>> dm['bob-balance'] 200.0 >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 >>> savepoint.rollback() # redundant, but should be harmless >>> dm['bob-balance'] 100.0 >>> dm['bob-balance'] = 300.0 >>> dm['bob-balance'] 300.0 >>> savepoint.rollback() >>> dm['bob-balance'] 100.0rRhR]rSh_X>>> dm['bob-balance'] = 100.0 >>> dm['bob-balance'] 100.0 >>> savepoint = transaction.savepoint() >>> dm['bob-balance'] = 200.0 >>> dm['bob-balance'] 200.0 >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 >>> savepoint.rollback() # redundant, but should be harmless >>> dm['bob-balance'] 100.0 >>> dm['bob-balance'] = 300.0 >>> dm['bob-balance'] 300.0 >>> savepoint.rollback() >>> dm['bob-balance'] 100.0rTrU}rV(hcjEh Uubahcj"ubhf)rW}rX(h4}rY(hC]rZh?]r[h6]r\h8]r]hA]r^uh hhPKh hoheh=h XIHowever, using a savepoint invalidates any savepoints that come after it:r_hR]r`h_XIHowever, using a savepoint invalidates any savepoints that come after it:rarb}rc(hcjWh j_ubahcj"ubhu)rd}re(h4}rf(UtestrgX_>>> dm['bob-balance'] = 200.0 >>> dm['bob-balance'] 200.0 >>> savepoint1 = transaction.savepoint() >>> dm['bob-balance'] = 300.0 >>> dm['bob-balance'] 300.0 >>> savepoint2 = transaction.savepoint() >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 >>> savepoint2.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... InvalidSavepointRollbackError: invalidated by a later savepoint >>> savepoint1.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... InvalidSavepointRollbackError: invalidated by a later savepoint >>> transaction.abort()rhU testnodetyperiXdoctestrjUgroupsrk]rlh}ah6]rmh8]rnh?]rohhh}rphA]rqhC]rruh hhPKh hheh=h X>>> dm['bob-balance'] = 200.0 >>> dm['bob-balance'] 200.0 >>> savepoint1 = transaction.savepoint() >>> dm['bob-balance'] = 300.0 >>> dm['bob-balance'] 300.0 >>> savepoint2 = transaction.savepoint() >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 >>> savepoint2.rollback() Traceback (most recent call last): ... InvalidSavepointRollbackError: invalidated by a later savepoint >>> savepoint1.rollback() Traceback (most recent call last): ... InvalidSavepointRollbackError: invalidated by a later savepoint >>> transaction.abort()rshR]rth_X>>> dm['bob-balance'] = 200.0 >>> dm['bob-balance'] 200.0 >>> savepoint1 = transaction.savepoint() >>> dm['bob-balance'] = 300.0 >>> dm['bob-balance'] 300.0 >>> savepoint2 = transaction.savepoint() >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 >>> savepoint2.rollback() Traceback (most recent call last): ... InvalidSavepointRollbackError: invalidated by a later savepoint >>> savepoint1.rollback() Traceback (most recent call last): ... InvalidSavepointRollbackError: invalidated by a later savepoint >>> transaction.abort()rurv}rw(hcjdh Uubahcj"ubeh Uheh=hcjubhG)rx}ry(h4}rz(hC]r{h?]r|h6]r}hah8]r~hA]rhauh hhPKh hQhR]r(hT)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPKh h]hR]rh_X#Databases without savepoint supportrr}r(hcjh X#Databases without savepoint supportrubah jheh=hcjxubhf)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPKh hoheh=h XVNormally it's an error to use savepoints with databases that don't support savepoints:rhR]rh_XVNormally it's an error to use savepoints with databases that don't support savepoints:rr}r(hcjh jubahcjxubhu)r}r(h4}r(jgX=>>> dm_no_sp = savepointsample.SampleDataManager() >>> dm_no_sp['name'] = 'bob' >>> transaction.commit() >>> dm_no_sp['name'] = 'sally' >>> transaction.savepoint() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'bob'}) >>> transaction.abort()rU testnodetyperXdoctestrUgroupsr]rh}ah6]rh8]rh?]rhhh}rhA]rhC]ruh hhPKh hheh=h X>>> dm_no_sp = savepointsample.SampleDataManager() >>> dm_no_sp['name'] = 'bob' >>> transaction.commit() >>> dm_no_sp['name'] = 'sally' >>> transaction.savepoint() Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'bob'}) >>> transaction.abort()rhR]rh_X>>> dm_no_sp = savepointsample.SampleDataManager() >>> dm_no_sp['name'] = 'bob' >>> transaction.commit() >>> dm_no_sp['name'] = 'sally' >>> transaction.savepoint() Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'bob'}) >>> transaction.abort()rr}r(hcjh Uubahcjxubhf)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPKh hoheh=h XHowever, a flag can be passed to the transaction savepoint method to indicate that databases without savepoint support should be tolerated until a savepoint is rolled back. This allows transactions to proceed if there are no reasons to roll back:rhR]rh_XHowever, a flag can be passed to the transaction savepoint method to indicate that databases without savepoint support should be tolerated until a savepoint is rolled back. This allows transactions to proceed if there are no reasons to roll back:rr}r(hcjh jubahcjxubhu)r}r(h4}r(jgXy>>> dm_no_sp['name'] = 'sally' >>> savepoint = transaction.savepoint(1) >>> dm_no_sp['name'] = 'sue' >>> transaction.commit() >>> dm_no_sp['name'] 'sue' >>> dm_no_sp['name'] = 'sam' >>> savepoint = transaction.savepoint(1) >>> savepoint.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'sam'})rU testnodetyperXdoctestrUgroupsr]rh}ah6]rh8]rh?]rhhh}rhA]rhC]ruh hhPKh hheh=h XW>>> dm_no_sp['name'] = 'sally' >>> savepoint = transaction.savepoint(1) >>> dm_no_sp['name'] = 'sue' >>> transaction.commit() >>> dm_no_sp['name'] 'sue' >>> dm_no_sp['name'] = 'sam' >>> savepoint = transaction.savepoint(1) >>> savepoint.rollback() Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'sam'})rhR]rh_XW>>> dm_no_sp['name'] = 'sally' >>> savepoint = transaction.savepoint(1) >>> dm_no_sp['name'] = 'sue' >>> transaction.commit() >>> dm_no_sp['name'] 'sue' >>> dm_no_sp['name'] = 'sam' >>> savepoint = transaction.savepoint(1) >>> savepoint.rollback() Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'sam'})rr}r(hcjh Uubahcjxubeh Uheh=hcjubhG)r}r(h4}r(hC]rh?]rh6]rhah8]rhA]rhauh hhPM h hQhR]r(hT)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPM h h]hR]rh_XFailuresrr}r(hcjh XFailuresrubah jheh=hcjubhf)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPM h hoheh=h XIf a failure occurs when creating or rolling back a savepoint, the transaction state will be uncertain and the transaction will become uncommitable. From that point on, most transaction operations, including commit, will fail until the transaction is aborted.rhR]rh_XIf a failure occurs when creating or rolling back a savepoint, the transaction state will be uncertain and the transaction will become uncommitable. From that point on, most transaction operations, including commit, will fail until the transaction is aborted.rr}r(hcjh jubahcjubhf)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPMh hoheh=h XIn the previous example, we got an error when we tried to rollback the savepoint. If we try to commit the transaction, the commit will fail:rhR]rh_XIn the previous example, we got an error when we tried to rollback the savepoint. If we try to commit the transaction, the commit will fail:rr}r(hcjh jubahcjubhu)r}r(h4}r(jgX>>> transaction.commit() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TransactionFailedError: An operation previously failed, with traceback: ... TypeError: ('Savepoints unsupported', {'name': 'sam'}) rU testnodetyperXdoctestrUgroupsr]rh}ah6]rh8]rh?]rhhh}r hA]r hC]r uh hhPMh hheh=h X>>> transaction.commit() Traceback (most recent call last): ... TransactionFailedError: An operation previously failed, with traceback: ... TypeError: ('Savepoints unsupported', {'name': 'sam'}) r hR]r h_X>>> transaction.commit() Traceback (most recent call last): ... TransactionFailedError: An operation previously failed, with traceback: ... TypeError: ('Savepoints unsupported', {'name': 'sam'}) rr}r(hcjh Uubahcjubhf)r}r(h4}r(hC]rh?]rh6]rh8]rhA]ruh hhPMh hoheh=h X)We have to abort it to make any progress:rhR]rh_X)We have to abort it to make any progress:rr}r(hcjh jubahcjubhu)r}r(h4}r (U testnodetyper!Xdoctestr"Ugroupsr#]r$h}ah6]r%h8]r&h?]r'hhh}r(hA]r)hC]r*uh hhPM h hheh=h X>>> transaction.abort()r+hR]r,h_X>>> transaction.abort()r-r.}r/(hcjh Uubahcjubhf)r0}r1(h4}r2(hC]r3h?]r4h6]r5h8]r6hA]r7uh hhPM$h hoheh=h XySimilarly, in our earlier example, where we tried to take a savepoint with a data manager that didn't support savepoints:r8hR]r9h_XySimilarly, in our earlier example, where we tried to take a savepoint with a data manager that didn't support savepoints:r:r;}r<(hcj0h j8ubahcjubhu)r=}r>(h4}r?(jgX>>> dm_no_sp['name'] = 'sally' >>> dm['name'] = 'sally' >>> savepoint = transaction.savepoint() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'sue'}) >>> transaction.commit() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TransactionFailedError: An operation previously failed, with traceback: ... TypeError: ('Savepoints unsupported', {'name': 'sue'}) >>> transaction.abort()r@U testnodetyperAXdoctestrBUgroupsrC]rDh}ah6]rEh8]rFh?]rGhhh}rHhA]rIhC]rJuh hhPM'h hheh=h X>>> dm_no_sp['name'] = 'sally' >>> dm['name'] = 'sally' >>> savepoint = transaction.savepoint() Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'sue'}) >>> transaction.commit() Traceback (most recent call last): ... TransactionFailedError: An operation previously failed, with traceback: ... TypeError: ('Savepoints unsupported', {'name': 'sue'}) >>> transaction.abort()rKhR]rLh_X>>> dm_no_sp['name'] = 'sally' >>> dm['name'] = 'sally' >>> savepoint = transaction.savepoint() Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'sue'}) >>> transaction.commit() Traceback (most recent call last): ... TransactionFailedError: An operation previously failed, with traceback: ... TypeError: ('Savepoints unsupported', {'name': 'sue'}) >>> transaction.abort()rMrN}rO(hcj=h Uubahcjubhf)rP}rQ(h4}rR(hC]rSh?]rTh6]rUh8]rVhA]rWuh hhPM:h hoheh=h XRAfter clearing the transaction with an abort, we can get on with new transactions:rXhR]rYh_XRAfter clearing the transaction with an abort, we can get on with new transactions:rZr[}r\(hcjPh jXubahcjubhu)r]}r^(h4}r_(U testnodetyper`XdoctestraUgroupsrb]rch}ah6]rdh8]reh?]rfhhh}rghA]rhhC]riuh hhPM=h hheh=h X>>> dm_no_sp['name'] = 'sally' >>> dm['name'] = 'sally' >>> transaction.commit() >>> dm_no_sp['name'] 'sally' >>> dm['name'] 'sally'rjhR]rkh_X>>> dm_no_sp['name'] = 'sally' >>> dm['name'] = 'sally' >>> transaction.commit() >>> dm_no_sp['name'] 'sally' >>> dm['name'] 'sally'rlrm}rn(hcj]h Uubahcjubeh Uheh=hcjubeh Uheh=hchububhjxhjhj"hjuU decorationroNhR]rpjaU footnote_refsrq}rrU current_linersNUsettingsrt(cdocutils.frontend Values ruorv}rw(U sectnum_xformrxKU halt_levelryKU datestamprzNU tab_widthr{KU toc_backlinksr|Uentryr}Uembed_stylesheetr~Ugettext_compactrU report_levelrKUstrip_commentsrNU source_linkrNUenvrNU smart_quotesrU_disable_configrNUsectsubtitle_xformrUinput_encoding_error_handlerrUstrictrU rfc_base_urlrUhttp://tools.ietf.org/html/rUfile_insertion_enabledrUdebugrNUwarning_streamrNU strip_classesrNUoutput_encodingrUutf-8rU docinfo_xformrKU source_urlrNUdoctitle_xformrUdump_internalsrNUfootnote_backlinksrKU id_prefixrUUpep_referencesrNh]NUauto_id_prefixrUidrUstrip_elements_with_classesrNUoutput_encoding_error_handlerrjU _config_filesr]rU pep_base_urlrUhttp://www.python.org/dev/peps/rU _destinationrNUerror_encodingrUUTF-8rU tracebackrUrfc_referencesrNUexpose_internalsrNUtrim_footnote_reference_spacerUrecord_dependenciesrNU_sourcerU?/home/tseaver/projects/Zope/ZODB/transaction/docs/savepoint.rstrU raw_enabledrKUconfigrNU generatorrNUerror_encoding_error_handlerrUbackslashreplacerU language_coderUenrUinput_encodingrU utf-8-sigrUstrict_visitorrNUsyntax_highlightrUlongrUexit_status_levelrKU dump_settingsrNUcloak_email_addressesrUdump_transformsrNUdump_pseudo_xmlrNUpep_file_url_templaterUpep-%04drubUtransform_messagesr]rUautofootnote_startrKub.transaction-1.4.3/docs/_build/doctrees/api.doctree0000664000175000017500000062352612312641212022100 0ustar tseavertseavercdocutils.nodes document q)q}q(Urefnamesq}qUsubstitution_defsq}qUautofootnote_refsq]q Utagnameq Udocumentq U rawsourceq UU nametypesq }q(X1transaction._manager.TransactionManager.savepointqX,transaction.interfaces.ISavepointDataManagerqX7transaction.interfaces.ITransaction.addBeforeCommitHookqX%transaction.interfaces.TransientErrorqX"transaction._transaction.SavepointqX'transaction.interfaces.ISavepoint.validqX-transaction._manager.TransactionManager.abortqX0transaction.interfaces.ITransactionManager.abortqX,transaction.interfaces.IDataManager.tpc_voteqX4transaction.interfaces.ISynchronizer.afterCompletionqX+transaction._transaction.Savepoint.rollbackqX8transaction.interfaces.ITransaction.getBeforeCommitHooksqX6transaction.interfaces.ITransaction.addAfterCommitHookqX0transaction._manager.TransactionManager.__exit__qX+transaction._manager.TransactionManager.getqX api objectsqNX!transaction.interfaces.ISavepointqX$transaction._transaction.Transactionq X)transaction.interfaces.ITransaction.abortq!X$transaction.interfaces.ISynchronizerq"X-transaction._manager.ThreadTransactionManagerq#X'transaction.interfaces.TransactionErrorq$X5transaction._manager.TransactionManager.registerSynchq%X3transaction.interfaces.ISynchronizer.newTransactionq&X8transaction.interfaces.ITransactionManager.registerSynchq'X4transaction.interfaces.ITransactionManager.savepointq(X-transaction.interfaces.IDataManager.tpc_beginq)X8transaction._transaction.Transaction.addBeforeCommitHookq*X(transaction.interfaces.ITransaction.doomq+X*transaction.interfaces.IDataManager.commitq,X*transaction.interfaces.ITransaction.commitq-X4transaction._transaction.Transaction.setExtendedInfoq.X.transaction.interfaces.IDataManager.tpc_finishq/X3transaction.interfaces.ITransactionManager.isDoomedq0X+transaction.interfaces.ITransaction.setUserq1X.transaction._transaction.Transaction.savepointq2X(transaction.interfaces.DoomedTransactionq3X,transaction._manager.TransactionManager.doomq4X5transaction.interfaces.ISynchronizer.beforeCompletionq5X8transaction._transaction.Transaction.getAfterCommitHooksq6X)transaction.interfaces.IDataManager.abortq7X#transaction.interfaces.ITransactionq8X7transaction.interfaces.ITransaction.getAfterCommitHooksq9X*transaction.interfaces.ITransactionManagerq:X-transaction._transaction.Transaction.registerq;X)transaction._transaction.Transaction.noteqX interfacesq?NX)transaction._transaction.Transaction.doomq@X(transaction.interfaces.ITransaction.userqAX,transaction.interfaces.IDataManagerSavepointqBX/transaction.interfaces.ITransactionManager.doomqCX-transaction._transaction.Transaction.isDoomedqDX(transaction.interfaces.ITransaction.noteqEX-transaction.interfaces.IDataManager.tpc_abortqFX'transaction._manager.TransactionManagerqGX7transaction.interfaces.IDataManager.transaction_managerqHX7transaction._transaction.Transaction.addAfterCommitHookqIX-transaction._manager.TransactionManager.beginqJX+transaction.interfaces.IDataManager.sortKeyqKX/transaction.interfaces.ITransaction.descriptionqLX3transaction.interfaces.ITransaction.setExtendedInfoqMX0transaction.interfaces.ITransactionManager.beginqNX5transaction.interfaces.IDataManagerSavepoint.rollbackqOX0transaction._manager.TransactionManager.isDoomedqPX.transaction._manager.TransactionManager.commitqQX7transaction._manager.TransactionManager.unregisterSynchqRX*transaction._transaction.Transaction.abortqSX-transaction.interfaces.TransactionFailedErrorqTX1transaction._manager.TransactionManager.__enter__qUX6transaction.interfaces.ISavepointDataManager.savepointqVX:transaction.interfaces.ITransactionManager.unregisterSynchqWX(transaction.interfaces.ITransaction.joinqXX.transaction.interfaces.ITransactionManager.getqYX)transaction._transaction.Transaction.joinqZX9transaction._transaction.Transaction.getBeforeCommitHooksq[X4transaction.interfaces.ITransaction.beforeCommitHookq\X4transaction.interfaces.InvalidSavepointRollbackErrorq]X,transaction._transaction.Transaction.setUserq^Xtransaction api referenceq_NX+transaction._transaction.Transaction.commitq`X#transaction.interfaces.IDataManagerqaX1transaction.interfaces.ITransactionManager.commitqbuUreporterqcNUsymbol_footnote_startqdKUid_startqeKh hUnameidsqf}qg(hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhU api-objectsqhhhh h h!h!h"h"h#h#h$h$h%h%h&h&h'h'h(h(h)h)h*h*h+h+h,h,h-h-h.h.h/h/h0h0h1h1h2h2h3h3h4h4h5h5h6h6h7h7h8h8h9h9h:h:h;h;hh>h?U interfacesqih@h@hAhAhBhBhChChDhDhEhEhFhFhGhGhHhHhIhIhJhJhKhKhLhLhMhMhNhNhOhOhPhPhQhQhRhRhShShThThUhUhVhVhWhWhXhXhYhYhZhZh[h[h\h\h]h]h^h^h_Utransaction-api-referenceqjh`h`hahahbhbuUsymbol_footnote_refsqk]qlUindirect_targetsqm]qnUcurrent_sourceqoNUparse_messagesqp]qqU autofootnotesqr]qsUrefidsqt}quU transformerqvNUsymbol_footnotesqw]qxU footnotesqy]qzU citation_refsq{}q|Usubstitution_namesq}}q~U citationsq]qU attributesq}q(Uidsq]qUdupnamesq]qUsourceqcdocutils.nodes reprunicode qX9/home/tseaver/projects/Zope/ZODB/transaction/docs/api.rstqq}qbUbackrefsq]qUnamesq]qUclassesq]quUidsq}q(hVcsphinx.addnodes desc_signature q)q}q(h}q(UfullnameqXISavepointDataManager.savepointqUmoduleqXtransaction.interfacesqh]qhVah]qh]qUclassqXISavepointDataManagerqh]qhVah]qUfirstquh hUlineqKh Udesc_signatureqUchildrenq]q(csphinx.addnodes desc_name q)q}q(h}q(h]qh]qh]qh]qh]quh hhKh U desc_nameqh]qcdocutils.nodes Text qX savepointqq}q(Uparentqhh Uubah X savepointqUsourceqXCdocstring of transaction.interfaces.ISavepointDataManager.savepointqhhubcsphinx.addnodes desc_parameterlist q)q}q(h}q(h]qh]qh]qh]qh]quh hhKh Udesc_parameterlistqh]qh Uhhhhubcsphinx.addnodes only q)q}q(h}q(h]qh]qh]qh]qh]qUexprqUhtmlquh hhNh Uonlyqh]qcsphinx.addnodes pending_xref q)q}q(hhh U pending_xrefqh]qcdocutils.nodes inline q)q}q(hhh Uinlineqh]qhX[source]q݅q}q(hhh Uubah Uh}q(h]qU viewcode-linkqah]qh]qh]qh]quubah Uh}q(UrefdocqXapiqUrefidqhh]qh]qU refexplicitqUreftypeqUviewcodeqh]qh]qU refdomainqUstdqh]qU reftargetqX_modules/transaction/interfacesquubah UhNhhubeh X savepoint()qhhhcsphinx.addnodes desc q)q}q(h}q(h]qh]qh]qUdesctypeqXmethodrUnoindexrh]rh]rUobjtyperjUdomainrXpyruh hhNh Udescrh]r(hcsphinx.addnodes desc_content r )r }r (h}r (h]r h]rh]rh]rh]ruh hhKh U desc_contentrh]rcdocutils.nodes paragraph r)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh U paragraphrhhh X8Return a data-manager savepoint (IDataManagerSavepoint).rh]rhX8Return a data-manager savepoint (IDataManagerSavepoint).r r!}r"(hjh jubahj ubah Uhhhhubeh Uhhhj )r#}r$(h}r%(h]r&h]r'h]r(h]r)h]r*uh hhNh jh]r+(j)r,}r-(h}r.(h]r/h]r0h]r1h]r2h]r3uh hhKh jhU r4h X5Extends: :class:`transaction.interfaces.IDataManager`r5h]r6(hX Extends: r7r8}r9(hj,h X Extends: r:ubh)r;}r<(h}r=(Urefdocr>hUrefwarnr?U py:moduler@hUpy:classrAhh]rBh]rCU refexplicitrDUreftyperEXclassrFh]rGh]rHU refdomainrIXpyrJh]rKU reftargetrLX#transaction.interfaces.IDataManagerrMuhKh hhhh X,:class:`transaction.interfaces.IDataManager`rNh]rOcdocutils.nodes literal rP)rQ}rR(hj;h UliteralrSh]rThX#transaction.interfaces.IDataManagerrUrV}rW(hjQh Uubah jNh}rX(h]rY(UxrefrZjJXpy-classr[eh]r\h]r]h]r^h]r_uubahj,ubehj#ubcsphinx.addnodes index r`)ra}rb(h}rc(h]rdh]reh]rfUentriesrg]rh(UsingleriXAsavepoint() (transaction.interfaces.ISavepointDataManager method)hVUtrjah]rkh]rluh hhNh Uindexrmh]rnh Uhhhj#ubheh UhU rohh)rp}rq(h}rr(h]rsh]rth]ruhX interfacervjh]rwh]rxjjvjXpyryuh hhNh jh]rz(h)r{}r|(h}r}(hhhhXtransaction.interfacesr~r}rbh]rhah]rh]rhUh]rhah]rhuh hhNh hh]r(csphinx.addnodes desc_annotation r)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh Udesc_annotationrh]rhX interface rr}r(hjh Uubah X interface rhjohj{ubcsphinx.addnodes desc_addname r)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh U desc_addnamerh]rhXtransaction.interfaces.rr}r(hjh Uubah Xtransaction.interfaces.rhjohj{ubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh hh]rhXISavepointDataManagerrr}r(hjh Uubah hhjohj{ubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrhh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhj{ubeh hhjohjpubj#eh UhNhcdocutils.nodes section r)r}r(h}r(h]rh]rh]r(Xmodule-transaction.interfacesrhieh]rh]rh?auh hhKh Usectionrh]r(cdocutils.nodes title r)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh Utitlerh]rhX Interfacesrr}r(hjh X Interfacesrubah jhhhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiXtransaction.interfaces (module)Xmodule-transaction.interfacesUtrah]rh]ruh hhKh jmh]rh Uhhhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX9ITransactionManager (interface in transaction.interfaces)h:Utrah]rh]ruh hhNh jmh]r h UhNhjubh)r }r (h}r (h]r h]rh]rhX interfacerjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXITransactionManagerrhhXtransaction.interfacesrr}rbh]rh:ah]rh]rhUh]rh:ah]r huh hhNh hh]r!(j)r"}r#(h}r$(h]r%h]r&h]r'h]r(h]r)uh hhNh jh]r*hX interface r+r,}r-(hj"h Uubah X interface r.hjohjubj)r/}r0(h}r1(h]r2h]r3h]r4h]r5h]r6uh hhNh jh]r7hXtransaction.interfaces.r8r9}r:(hj/h Uubah Xtransaction.interfaces.r;hjohjubh)r<}r=(h}r>(h]r?h]r@h]rAh]rBh]rCuh hhNh hh]rDhXITransactionManagerrErF}rG(hj<h Uubah jhjohjubh)rH}rI(h}rJ(h]rKh]rLh]rMh]rNh]rOUexprrPhuh hhNh hh]rQh)rR}rS(hjHh hh]rTh)rU}rV(hjRh hh]rWhX[source]rXrY}rZ(hjUh Uubah Uh}r[(h]r\hah]r]h]r^h]r_h]r`uubah Uh}ra(UrefdocrbhUrefidrcjh]rdh]reU refexplicitrfUreftyperghh]rhh]riU refdomainrjhh]rkU reftargetrlX_modules/transaction/interfacesrmuubah UhNhjubeh jhjohj ubj )rn}ro(h}rp(h]rqh]rrh]rsh]rth]ruuh hhNh jh]rv(j)rw}rx(h}ry(h]rzh]r{h]r|h]r}h]r~uh hhKh jhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.ITransactionManagerrh X2An object that manages a sequence of transactions.rh]rhX2An object that manages a sequence of transactions.rr}r(hjwh jubahjnubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XJApplications use transaction managers to establish transaction boundaries.rh]rhXJApplications use transaction managers to establish transaction boundaries.rr}r(hjh jubahjnubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX;begin() (transaction.interfaces.ITransactionManager method)hNUtrah]rh]ruh hhNh jmh]rh UhX=docstring of transaction.interfaces.ITransactionManager.beginrhjnubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXITransactionManager.beginrhhh]rhNah]rh]rhjh]rhNah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXbeginrr}r(hjh Uubah XbeginrhUrhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rh Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh Xbegin()rhjhjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XBegin a new transaction.rh]rhXBegin a new transaction.rr}r(hjh jubahjubj)r}r (h}r (h]r h]r h]r h]rh]ruh hhKh jhjh X>If an existing transaction is in progress, it will be aborted.rh]rhX>If an existing transaction is in progress, it will be aborted.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XfThe newTransaction() method of registered synchronizers is called, passing the new transaction object.rh]rhXfThe newTransaction() method of registered synchronizers is called, passing the new transaction object.rr }r!(hjh jubahjubeh Uhjhjubeh Uhjhjnubj`)r"}r#(h}r$(h]r%h]r&h]r'Uentriesr(]r)(jiX9get() (transaction.interfaces.ITransactionManager method)hYUtr*ah]r+h]r,uh hhNh jmh]r-h UhX;docstring of transaction.interfaces.ITransactionManager.getr.hjnubh)r/}r0(h}r1(h]r2h]r3h]r4hXmethodr5jh]r6h]r7jj5jXpyr8uh hhNh jh]r9(h)r:}r;(h}r<(hXITransactionManager.getr=hhh]r>hYah]r?h]r@hjh]rAhYah]rBhuh hhKh hh]rC(h)rD}rE(h}rF(h]rGh]rHh]rIh]rJh]rKuh hhKh hh]rLhXgetrMrN}rO(hjDh Uubah XgetrPhjhj:ubh)rQ}rR(h}rS(h]rTh]rUh]rVh]rWh]rXuh hhKh hh]rYh Uhjhj:ubh)rZ}r[(h}r\(h]r]h]r^h]r_h]r`h]raUexprrbhuh hhNh hh]rch)rd}re(hjZh hh]rfh)rg}rh(hjdh hh]rihX[source]rjrk}rl(hjgh Uubah Uh}rm(h]rnhah]roh]rph]rqh]rruubah Uh}rs(UrefdocrthUrefidruj=h]rvh]rwU refexplicitrxUreftyperyhh]rzh]r{U refdomainr|hh]r}U reftargetr~X_modules/transaction/interfacesruubah UhNhj:ubeh Xget()rhjhj/ubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]rj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhj.h XGet the current transaction.rh]rhXGet the current transaction.rr}r(hjh jubahjubah Uhjhj/ubeh Uhj.hjnubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX<commit() (transaction.interfaces.ITransactionManager method)hbUtrah]rh]ruh hhNh jmh]rh UhX>docstring of transaction.interfaces.ITransactionManager.commitrhjnubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXITransactionManager.commitrhhh]rhbah]rh]rhjh]rhbah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXcommitrr}r(hjh Uubah Xcommitrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rh Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh Xcommit()rhjhjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]rj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XCommit the current transaction.rh]rhXCommit the current transaction.r r }r (hjh jubahjubah Uhjhjubeh Uhjhjnubj`)r }r (h}r(h]rh]rh]rUentriesr]r(jiX;abort() (transaction.interfaces.ITransactionManager method)hUtrah]rh]ruh hhNh jmh]rh UhX=docstring of transaction.interfaces.ITransactionManager.abortrhjnubh)r}r(h}r(h]rh]rh]rhXmethodrjh]r h]r!jjjXpyr"uh hhNh jh]r#(h)r$}r%(h}r&(hXITransactionManager.abortr'hhh]r(hah]r)h]r*hjh]r+hah]r,huh hhKh hh]r-(h)r.}r/(h}r0(h]r1h]r2h]r3h]r4h]r5uh hhKh hh]r6hXabortr7r8}r9(hj.h Uubah Xabortr:hjhj$ubh)r;}r<(h}r=(h]r>h]r?h]r@h]rAh]rBuh hhKh hh]rCh Uhjhj$ubh)rD}rE(h}rF(h]rGh]rHh]rIh]rJh]rKUexprrLhuh hhNh hh]rMh)rN}rO(hjDh hh]rPh)rQ}rR(hjNh hh]rShX[source]rTrU}rV(hjQh Uubah Uh}rW(h]rXhah]rYh]rZh]r[h]r\uubah Uh}r](Urefdocr^hUrefidr_j'h]r`h]raU refexplicitrbUreftyperchh]rdh]reU refdomainrfhh]rgU reftargetrhX_modules/transaction/interfacesriuubah UhNhj$ubeh Xabort()rjhjhjubj )rk}rl(h}rm(h]rnh]roh]rph]rqh]rruh hhKh jh]rsj)rt}ru(h}rv(h]rwh]rxh]ryh]rzh]r{uh hhKh jhjh XAbort the current transaction.r|h]r}hXAbort the current transaction.r~r}r(hjth j|ubahjkubah Uhjhjubeh Uhjhjnubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX:doom() (transaction.interfaces.ITransactionManager method)hCUtrah]rh]ruh hhNh jmh]rh UhX<docstring of transaction.interfaces.ITransactionManager.doomrhjnubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXITransactionManager.doomrhhh]rhCah]rh]rhjh]rhCah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXdoomrr}r(hjh Uubah Xdoomrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rh Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh Xdoom()rhjhjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]rj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XDoom the current transaction.rh]rhXDoom the current transaction.rr}r(hjh jubahjubah Uhjhjubeh Uhjhjnubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX>isDoomed() (transaction.interfaces.ITransactionManager method)h0Utrah]rh]ruh hhNh jmh]rh UhX@docstring of transaction.interfaces.ITransactionManager.isDoomedrhjnubh)r}r(h}r(h]rh]rh]rhXmethodr jh]r h]r jj jXpyr uh hhNh jh]r (h)r}r(h}r(hXITransactionManager.isDoomedrhhh]rh0ah]rh]rhjh]rh0ah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]r hXisDoomedr!r"}r#(hjh Uubah XisDoomedr$hjhjubh)r%}r&(h}r'(h]r(h]r)h]r*h]r+h]r,uh hhKh hh]r-h Uhjhjubh)r.}r/(h}r0(h]r1h]r2h]r3h]r4h]r5Uexprr6huh hhNh hh]r7h)r8}r9(hj.h hh]r:h)r;}r<(hj8h hh]r=hX[source]r>r?}r@(hj;h Uubah Uh}rA(h]rBhah]rCh]rDh]rEh]rFuubah Uh}rG(UrefdocrHhUrefidrIjh]rJh]rKU refexplicitrLUreftyperMhh]rNh]rOU refdomainrPhh]rQU reftargetrRX_modules/transaction/interfacesrSuubah UhNhjubeh X isDoomed()rThjhjubj )rU}rV(h}rW(h]rXh]rYh]rZh]r[h]r\uh hhKh jh]r]j)r^}r_(h}r`(h]rah]rbh]rch]rdh]reuh hhKh jhjh XCReturns True if the current transaction is doomed, otherwise False.rfh]rghXCReturns True if the current transaction is doomed, otherwise False.rhri}rj(hj^h jfubahjUubah Uhjhjubeh Uhjhjnubj`)rk}rl(h}rm(h]rnh]roh]rpUentriesrq]rr(jiX?savepoint() (transaction.interfaces.ITransactionManager method)h(Utrsah]rth]ruuh hhNh jmh]rvh UhXAdocstring of transaction.interfaces.ITransactionManager.savepointrwhjnubh)rx}ry(h}rz(h]r{h]r|h]r}hXmethodr~jh]rh]rjj~jXpyruh hhNh jh]r(h)r}r(h}r(hXITransactionManager.savepointrhhh]rh(ah]rh]rhjh]rh(ah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhX savepointrr}r(hjh Uubah X savepointrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rcsphinx.addnodes desc_parameter r)r}r(hjh Udesc_parameterrh]rhXoptimistic=Falserr}r(hjh Uubah Xoptimistic=Falserh}r(h]rh]rh]rh]rh]ruubah Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh Xsavepoint(optimistic=False)rhjhjxubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjwh X0Create a savepoint from the current transaction.rh]rhX0Create a savepoint from the current transaction.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjwh XIf the optimistic argument is true, then data managers that don't support savepoints can be used, but an error will be raised if the savepoint is rolled back.rh]rhXIf the optimistic argument is true, then data managers that don't support savepoints can be used, but an error will be raised if the savepoint is rolled back.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjwh X!An ISavepoint object is returned.rh]rhX!An ISavepoint object is returned.rr}r(hjh jubahjubeh Uhjhjxubeh Uhjwhjnubj`)r }r (h}r (h]r h]r h]rUentriesr]r(jiXCregisterSynch() (transaction.interfaces.ITransactionManager method)h'Utrah]rh]ruh hhNh jmh]rh UhXEdocstring of transaction.interfaces.ITransactionManager.registerSynchrhjnubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r (h)r!}r"(h}r#(hX!ITransactionManager.registerSynchr$hhh]r%h'ah]r&h]r'hjh]r(h'ah]r)huh hhKh hh]r*(h)r+}r,(h}r-(h]r.h]r/h]r0h]r1h]r2uh hhKh hh]r3hX registerSynchr4r5}r6(hj+h Uubah X registerSynchr7hjhj!ubh)r8}r9(h}r:(h]r;h]r<h]r=h]r>h]r?uh hhKh hh]r@j)rA}rB(hj8h jh]rChXsynchrDrE}rF(hjAh Uubah XsynchrGh}rH(h]rIh]rJh]rKh]rLh]rMuubah Uhjhj!ubh)rN}rO(h}rP(h]rQh]rRh]rSh]rTh]rUUexprrVhuh hhNh hh]rWh)rX}rY(hjNh hh]rZh)r[}r\(hjXh hh]r]hX[source]r^r_}r`(hj[h Uubah Uh}ra(h]rbhah]rch]rdh]reh]rfuubah Uh}rg(UrefdocrhhUrefidrij$h]rjh]rkU refexplicitrlUreftypermhh]rnh]roU refdomainrphh]rqU reftargetrrX_modules/transaction/interfacesrsuubah UhNhj!ubeh XregisterSynch(synch)rthjhjubj )ru}rv(h}rw(h]rxh]ryh]rzh]r{h]r|uh hhKh jh]r}(j)r~}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XRegister an ISynchronizer.rh]rhXRegister an ISynchronizer.rr}r(hj~h jubahjuubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XkSynchronizers are notified about some major events in a transaction's life. See ISynchronizer for details.rh]rhXkSynchronizers are notified about some major events in a transaction's life. See ISynchronizer for details.rr}r(hjh jubahjuubeh Uhjhjubeh Uhjhjnubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiXEunregisterSynch() (transaction.interfaces.ITransactionManager method)hWUtrah]rh]ruh hhNh jmh]rh UhXGdocstring of transaction.interfaces.ITransactionManager.unregisterSynchrhjnubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hX#ITransactionManager.unregisterSynchrhhh]rhWah]rh]rhjh]rhWah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXunregisterSynchrr}r(hjh Uubah XunregisterSynchrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rj)r}r(hjh jh]rhXsynchrr}r(hjh Uubah Xsynchrh}r(h]rh]rh]rh]rh]ruubah Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh XunregisterSynch(synch)rhjhjubj )r}r(h}r(h]rh]rh]r h]r h]r uh hhKh jh]r (j)r }r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XUnregister an ISynchronizer.rh]rhXUnregister an ISynchronizer.rr}r(hj h jubahjubj)r}r(h}r(h]rh]rh]rh]r h]r!uh hhKh jhjh XkSynchronizers are notified about some major events in a transaction's life. See ISynchronizer for details.r"h]r#hXkSynchronizers are notified about some major events in a transaction's life. See ISynchronizer for details.r$r%}r&(hjh j"ubahjubeh Uhjhjubeh Uhjhjnubeh Uhjohj ubeh UhNhjubj`)r'}r((h}r)(h]r*h]r+h]r,Uentriesr-]r.(jiX2ITransaction (interface in transaction.interfaces)h8Utr/ah]r0h]r1uh hhNh jmh]r2h UhNhjubh)r3}r4(h}r5(h]r6h]r7h]r8hX interfacer9jh]r:h]r;jj9jXpyr<uh hhNh jh]r=(h)r>}r?(h}r@(hX ITransactionrAhhXtransaction.interfacesrBrC}rDbh]rEh8ah]rFh]rGhUh]rHh8ah]rIhuh hhNh hh]rJ(j)rK}rL(h}rM(h]rNh]rOh]rPh]rQh]rRuh hhNh jh]rShX interface rTrU}rV(hjKh Uubah X interface rWhjohj>ubj)rX}rY(h}rZ(h]r[h]r\h]r]h]r^h]r_uh hhNh jh]r`hXtransaction.interfaces.rarb}rc(hjXh Uubah Xtransaction.interfaces.rdhjohj>ubh)re}rf(h}rg(h]rhh]rih]rjh]rkh]rluh hhNh hh]rmhX ITransactionrnro}rp(hjeh Uubah jAhjohj>ubh)rq}rr(h}rs(h]rth]ruh]rvh]rwh]rxUexprryhuh hhNh hh]rzh)r{}r|(hjqh hh]r}h)r~}r(hj{h hh]rhX[source]rr}r(hj~h Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjAh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhj>ubeh jAhjohj3ubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.ITransactionrh X*Object representing a running transaction.rh]rhX*Object representing a running transaction.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XObjects with this interface may represent different transactions during their lifetime (.begin() can be called to start a new transaction using the same instance, although that example is deprecated and will go away in ZODB 3.6).rh]rhXObjects with this interface may represent different transactions during their lifetime (.begin() can be called to start a new transaction using the same instance, although that example is deprecated and will go away in ZODB 3.6).rr}r(hjh jubahjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX4user (transaction.interfaces.ITransaction attribute)hAUtrah]rh]ruh hhNh jmh]rh UhX5docstring of transaction.interfaces.ITransaction.userrhjubh)r}r(h}r(h]rh]rh]rhX attributerjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXITransaction.userrhhh]rhAah]rh]rhjAh]rhAah]rhuh hhKh hh]rh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXuserrr}r(hjh Uubah Xuserrhjhjubah jhjhjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh X,A user name associated with the transaction.rh]rhX,A user name associated with the transaction.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XThe format of the user name is defined by the application. The value is of Python type str. Storages record the user value, as meta-data, when a transaction commits.rh]r hXThe format of the user name is defined by the application. The value is of Python type str. Storages record the user value, as meta-data, when a transaction commits.r r }r (hjh jubahjubj)r }r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XA storage may impose a limit on the size of the value; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or truncate the value).rh]rhXA storage may impose a limit on the size of the value; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or truncate the value).rr}r(hj h jubahjubeh Uhjhjubeh Uhjhjubj`)r}r(h}r(h]rh]rh]rUentriesr ]r!(jiX;description (transaction.interfaces.ITransaction attribute)hLUtr"ah]r#h]r$uh hhNh jmh]r%h UhX<docstring of transaction.interfaces.ITransaction.descriptionr&hjubh)r'}r((h}r)(h]r*h]r+h]r,hX attributer-jh]r.h]r/jj-jXpyr0uh hhNh jh]r1(h)r2}r3(h}r4(hXITransaction.descriptionr5hhh]r6hLah]r7h]r8hjAh]r9hLah]r:huh hhKh hh]r;h)r<}r=(h}r>(h]r?h]r@h]rAh]rBh]rCuh hhKh hh]rDhX descriptionrErF}rG(hj<h Uubah X descriptionrHhjhj2ubah jHhjhj'ubj )rI}rJ(h}rK(h]rLh]rMh]rNh]rOh]rPuh hhKh jh]rQ(j)rR}rS(h}rT(h]rUh]rVh]rWh]rXh]rYuh hhKh jhj&h X)A textual description of the transaction.rZh]r[hX)A textual description of the transaction.r\r]}r^(hjRh jZubahjIubj)r_}r`(h}ra(h]rbh]rch]rdh]reh]rfuh hhKh jhj&h XThe value is of Python type str. Method note() is the intended way to set the value. Storages record the description, as meta-data, when a transaction commits.rgh]rhhXThe value is of Python type str. Method note() is the intended way to set the value. Storages record the description, as meta-data, when a transaction commits.rirj}rk(hj_h jgubahjIubj)rl}rm(h}rn(h]roh]rph]rqh]rrh]rsuh hhKh jhj&h XA storage may impose a limit on the size of the description; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or truncate the value).rth]ruhXA storage may impose a limit on the size of the description; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or truncate the value).rvrw}rx(hjlh jtubahjIubeh Uhjhj'ubeh Uhj&hjubj`)ry}rz(h}r{(h]r|h]r}h]r~Uentriesr]r(jiX5commit() (transaction.interfaces.ITransaction method)h-Utrah]rh]ruh hhNh jmh]rh UhX7docstring of transaction.interfaces.ITransaction.commitrhjubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXITransaction.commitrhhh]rh-ah]rh]rhjAh]rh-ah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXcommitrr}r(hjh Uubah Xcommitrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rh Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh Xcommit()rhjhjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XFinalize the transaction.rh]rhXFinalize the transaction.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XjThis executes the two-phase commit algorithm for all IDataManager objects associated with the transaction.rh]rhXjThis executes the two-phase commit algorithm for all IDataManager objects associated with the transaction.rr}r(hjh jubahjubeh Uhjhjubeh Uhjhjubj`)r}r(h}r(h]rh]rh]r Uentriesr ]r (jiX4abort() (transaction.interfaces.ITransaction method)h!Utr ah]r h]r uh hhNh jmh]r h UhX6docstring of transaction.interfaces.ITransaction.abortr hjubh)r }r (h}r (h]r h]r h]r hXmethodr jh]r h]r jj jXpyr uh hhNh jh]r (h)r }r (h}r (hXITransaction.abortr hhh]r h!ah]r h]r hjAh]r h!ah]r huh hhKh hh]r (h)r }r (h}r (h]r h]r! h]r" h]r# h]r$ uh hhKh hh]r% hXabortr& r' }r( (hj h Uubah Xabortr) hjhj ubh)r* }r+ (h}r, (h]r- h]r. h]r/ h]r0 h]r1 uh hhKh hh]r2 h Uhjhj ubh)r3 }r4 (h}r5 (h]r6 h]r7 h]r8 h]r9 h]r: Uexprr; huh hhNh hh]r< h)r= }r> (hj3 h hh]r? h)r@ }rA (hj= h hh]rB hX[source]rC rD }rE (hj@ h Uubah Uh}rF (h]rG hah]rH h]rI h]rJ h]rK uubah Uh}rL (UrefdocrM hUrefidrN j h]rO h]rP U refexplicitrQ UreftyperR hh]rS h]rT U refdomainrU hh]rV U reftargetrW X_modules/transaction/interfacesrX uubah UhNhj ubeh Xabort()rY hjhj ubj )rZ }r[ (h}r\ (h]r] h]r^ h]r_ h]r` h]ra uh hhKh jh]rb (j)rc }rd (h}re (h]rf h]rg h]rh h]ri h]rj uh hhKh jhj h XAbort the transaction.rk h]rl hXAbort the transaction.rm rn }ro (hjc h jk ubahjZ ubj)rp }rq (h}rr (h]rs h]rt h]ru h]rv h]rw uh hhKh jhj h XtThis is called from the application. This can only be called before the two-phase commit protocol has been started.rx h]ry hXtThis is called from the application. This can only be called before the two-phase commit protocol has been started.rz r{ }r| (hjp h jx ubahjZ ubeh Uhjhj ubeh Uhj hjubj`)r} }r~ (h}r (h]r h]r h]r Uentriesr ]r (jiX3doom() (transaction.interfaces.ITransaction method)h+Utr ah]r h]r uh hhNh jmh]r h UhX5docstring of transaction.interfaces.ITransaction.doomr hjubh)r }r (h}r (h]r h]r h]r hXmethodr jh]r h]r jj jXpyr uh hhNh jh]r (h)r }r (h}r (hXITransaction.doomr hhh]r h+ah]r h]r hjAh]r h+ah]r huh hhKh hh]r (h)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh hh]r hXdoomr r }r (hj h Uubah Xdoomr hjhj ubh)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh hh]r h Uhjhj ubh)r }r (h}r (h]r h]r h]r h]r h]r Uexprr huh hhNh hh]r h)r }r (hj h hh]r h)r }r (hj h hh]r hX[source]r r }r (hj h Uubah Uh}r (h]r hah]r h]r h]r h]r uubah Uh}r (Urefdocr hUrefidr j h]r h]r U refexplicitr Ureftyper hh]r h]r U refdomainr hh]r U reftargetr X_modules/transaction/interfacesr uubah UhNhj ubeh Xdoom()r hjhj ubj )r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jh]r (j)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhj h XDoom the transaction.r h]r hXDoom the transaction.r r }r (hj h j ubahj ubj)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhj h XDooms the current transaction. This will cause DoomedTransactionException to be raised on any attempt to commit the transaction.r h]r hXDooms the current transaction. This will cause DoomedTransactionException to be raised on any attempt to commit the transaction.r r }r (hj h j ubahj ubj)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhj h X:Otherwise the transaction will behave as if it was active.r h]r hX:Otherwise the transaction will behave as if it was active.r r }r (hj h j ubahj ubeh Uhjhj ubeh Uhj hjubj`)r }r (h}r (h]r h]r h]r Uentriesr ]r (jiX8savepoint() (transaction.interfaces.ITransaction method)h>Utr ah]r h]r uh hhNh jmh]r h UhX:docstring of transaction.interfaces.ITransaction.savepointr hjubh)r }r (h}r (h]r h]r h]r hXmethodr jh]r h]r! jj jXpyr" uh hhNh jh]r# (h)r$ }r% (h}r& (hXITransaction.savepointr' hhh]r( h>ah]r) h]r* hjAh]r+ h>ah]r, huh hhKh hh]r- (h)r. }r/ (h}r0 (h]r1 h]r2 h]r3 h]r4 h]r5 uh hhKh hh]r6 hX savepointr7 r8 }r9 (hj. h Uubah X savepointr: hjhj$ ubh)r; }r< (h}r= (h]r> h]r? h]r@ h]rA h]rB uh hhKh hh]rC j)rD }rE (hj; h jh]rF hXoptimistic=FalserG rH }rI (hjD h Uubah Xoptimistic=FalserJ h}rK (h]rL h]rM h]rN h]rO h]rP uubah Uhjhj$ ubh)rQ }rR (h}rS (h]rT h]rU h]rV h]rW h]rX UexprrY huh hhNh hh]rZ h)r[ }r\ (hjQ h hh]r] h)r^ }r_ (hj[ h hh]r` hX[source]ra rb }rc (hj^ h Uubah Uh}rd (h]re hah]rf h]rg h]rh h]ri uubah Uh}rj (Urefdocrk hUrefidrl j' h]rm h]rn U refexplicitro Ureftyperp hh]rq h]rr U refdomainrs hh]rt U reftargetru X_modules/transaction/interfacesrv uubah UhNhj$ ubeh Xsavepoint(optimistic=False)rw hjhj ubj )rx }ry (h}rz (h]r{ h]r| h]r} h]r~ h]r uh hhKh jh]r (j)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhj h XCreate a savepoint.r h]r hXCreate a savepoint.r r }r (hj h j ubahjx ubj)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhj h XIf the optimistic argument is true, then data managers that don't support savepoints can be used, but an error will be raised if the savepoint is rolled back.r h]r hXIf the optimistic argument is true, then data managers that don't support savepoints can be used, but an error will be raised if the savepoint is rolled back.r r }r (hj h j ubahjx ubj)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhj h X!An ISavepoint object is returned.r h]r hX!An ISavepoint object is returned.r r }r (hj h j ubahjx ubeh Uhjhj ubeh Uhj hjubj`)r }r (h}r (h]r h]r h]r Uentriesr ]r (jiX3join() (transaction.interfaces.ITransaction method)hXUtr ah]r h]r uh hhNh jmh]r h UhX5docstring of transaction.interfaces.ITransaction.joinr hjubh)r }r (h}r (h]r h]r h]r hXmethodr jh]r h]r jj jXpyr uh hhNh jh]r (h)r }r (h}r (hXITransaction.joinr hhh]r hXah]r h]r hjAh]r hXah]r huh hhKh hh]r (h)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh hh]r hXjoinr r }r (hj h Uubah Xjoinr hjhj ubh)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh hh]r j)r }r (hj h jh]r hX datamanagerr r }r (hj h Uubah X datamanagerr h}r (h]r h]r h]r h]r h]r uubah Uhjhj ubh)r }r (h}r (h]r h]r h]r h]r h]r Uexprr huh hhNh hh]r h)r }r (hj h hh]r h)r }r (hj h hh]r hX[source]r r }r (hj h Uubah Uh}r (h]r hah]r h]r h]r h]r uubah Uh}r (Urefdocr hUrefidr j h]r h]r U refexplicitr Ureftyper hh]r h]r U refdomainr hh]r U reftargetr X_modules/transaction/interfacesr uubah UhNhj ubeh Xjoin(datamanager)r hjhj ubj )r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jh]r (j)r }r (h}r (h]r h]r! h]r" h]r# h]r$ uh hhKh jhj h X&Add a data manager to the transaction.r% h]r& hX&Add a data manager to the transaction.r' r( }r) (hj h j% ubahj ubj)r* }r+ (h}r, (h]r- h]r. h]r/ h]r0 h]r1 uh hhKh jhj h XN`datamanager` must provide the transactions.interfaces.IDataManager interface.r2 h]r3 (cdocutils.nodes title_reference r4 )r5 }r6 (hj* h Utitle_referencer7 h]r8 hX datamanagerr9 r: }r; (hj5 h Uubah X `datamanager`r< h}r= (h]r> h]r? h]r@ h]rA h]rB uubhXA must provide the transactions.interfaces.IDataManager interface.rC rD }rE (hj* h XA must provide the transactions.interfaces.IDataManager interface.rF ubehj ubeh Uhjhj ubeh Uhj hjubj`)rG }rH (h}rI (h]rJ h]rK h]rL UentriesrM ]rN (jiX3note() (transaction.interfaces.ITransaction method)hEUtrO ah]rP h]rQ uh hhNh jmh]rR h UhX5docstring of transaction.interfaces.ITransaction.noterS hjubh)rT }rU (h}rV (h]rW h]rX h]rY hXmethodrZ jh]r[ h]r\ jjZ jXpyr] uh hhNh jh]r^ (h)r_ }r` (h}ra (hXITransaction.noterb hhh]rc hEah]rd h]re hjAh]rf hEah]rg huh hhKh hh]rh (h)ri }rj (h}rk (h]rl h]rm h]rn h]ro h]rp uh hhKh hh]rq hXnoterr rs }rt (hji h Uubah Xnoteru hjhj_ ubh)rv }rw (h}rx (h]ry h]rz h]r{ h]r| h]r} uh hhKh hh]r~ j)r }r (hjv h jh]r hXtextr r }r (hj h Uubah Xtextr h}r (h]r h]r h]r h]r h]r uubah Uhjhj_ ubh)r }r (h}r (h]r h]r h]r h]r h]r Uexprr huh hhNh hh]r h)r }r (hj h hh]r h)r }r (hj h hh]r hX[source]r r }r (hj h Uubah Uh}r (h]r hah]r h]r h]r h]r uubah Uh}r (Urefdocr hUrefidr jb h]r h]r U refexplicitr Ureftyper hh]r h]r U refdomainr hh]r U reftargetr X_modules/transaction/interfacesr uubah UhNhj_ ubeh X note(text)r hjhjT ubj )r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jh]r (j)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhjS h X(Add text to the transaction description.r h]r hX(Add text to the transaction description.r r }r (hj h j ubahj ubj)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhjS h X#This modifies the `.description` attribute; see its docs for more detail. First surrounding whitespace is stripped from `text`. If `.description` is currently an empty string, then the stripped text becomes its value, else two newlines and the stripped text are appended to `.description`.r h]r (hXThis modifies the r r }r (hj h XThis modifies the r ubj4 )r }r (hj h j7 h]r hX .descriptionr r }r (hj h Uubah X`.description`r h}r (h]r h]r h]r h]r h]r uubhXY attribute; see its docs for more detail. First surrounding whitespace is stripped from r r }r (hj h XY attribute; see its docs for more detail. First surrounding whitespace is stripped from r ubj4 )r }r (hj h j7 h]r hXtextr r }r (hj h Uubah X`text`r h}r (h]r h]r h]r h]r h]r uubhX. If r r }r (hj h X. If r ubj4 )r }r (hj h j7 h]r hX .descriptionr r }r (hj h Uubah X`.description`r h}r (h]r h]r h]r h]r h]r uubhX is currently an empty string, then the stripped text becomes its value, else two newlines and the stripped text are appended to r r }r (hj h X is currently an empty string, then the stripped text becomes its value, else two newlines and the stripped text are appended to r ubj4 )r }r (hj h j7 h]r hX .descriptionr r }r (hj h Uubah X`.description`r h}r (h]r h]r h]r h]r h]r uubhX.r }r (hj h X.ubehj ubeh UhjhjT ubeh UhjS hjubj`)r }r (h}r (h]r h]r h]r Uentriesr ]r (jiX6setUser() (transaction.interfaces.ITransaction method)h1Utr! ah]r" h]r# uh hhNh jmh]r$ h UhX8docstring of transaction.interfaces.ITransaction.setUserr% hjubh)r& }r' (h}r( (h]r) h]r* h]r+ hXmethodr, jh]r- h]r. jj, jXpyr/ uh hhNh jh]r0 (h)r1 }r2 (h}r3 (hXITransaction.setUserr4 hhh]r5 h1ah]r6 h]r7 hjAh]r8 h1ah]r9 huh hhKh hh]r: (h)r; }r< (h}r= (h]r> h]r? h]r@ h]rA h]rB uh hhKh hh]rC hXsetUserrD rE }rF (hj; h Uubah XsetUserrG hjhj1 ubh)rH }rI (h}rJ (h]rK h]rL h]rM h]rN h]rO uh hhKh hh]rP (j)rQ }rR (hjH h jh]rS hX user_namerT rU }rV (hjQ h Uubah X user_namerW h}rX (h]rY h]rZ h]r[ h]r\ h]r] uubj)r^ }r_ (hjH h jh]r` hXpath='/'ra rb }rc (hj^ h Uubah Xpath='/'rd h}re (h]rf h]rg h]rh h]ri h]rj uubeh Uhjhj1 ubh)rk }rl (h}rm (h]rn h]ro h]rp h]rq h]rr Uexprrs huh hhNh hh]rt h)ru }rv (hjk h hh]rw h)rx }ry (hju h hh]rz hX[source]r{ r| }r} (hjx h Uubah Uh}r~ (h]r hah]r h]r h]r h]r uubah Uh}r (Urefdocr hUrefidr j4 h]r h]r U refexplicitr Ureftyper hh]r h]r U refdomainr hh]r U reftargetr X_modules/transaction/interfacesr uubah UhNhj1 ubeh XsetUser(user_name, path='/')r hjhj& ubj )r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jh]r (j)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhj% h XSet the user name.r h]r hXSet the user name.r r }r (hj h j ubahj ubj)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhj% h Xpath should be provided if needed to further qualify the identified user. This is a convenience method used by Zope. It sets the .user attribute to str(path) + " " + str(user_name). This sets the `.user` attribute; see its docs for more detail.r h]r (hXpath should be provided if needed to further qualify the identified user. This is a convenience method used by Zope. It sets the .user attribute to str(path) + " " + str(user_name). This sets the r r }r (hj h Xpath should be provided if needed to further qualify the identified user. This is a convenience method used by Zope. It sets the .user attribute to str(path) + " " + str(user_name). This sets the r ubj4 )r }r (hj h j7 h]r hX.userr r }r (hj h Uubah X`.user`r h}r (h]r h]r h]r h]r h]r uubhX) attribute; see its docs for more detail.r r }r (hj h X) attribute; see its docs for more detail.r ubehj ubeh Uhjhj& ubeh Uhj% hjubj`)r }r (h}r (h]r h]r h]r Uentriesr ]r (jiX>setExtendedInfo() (transaction.interfaces.ITransaction method)hMUtr ah]r h]r uh hhNh jmh]r h UhX@docstring of transaction.interfaces.ITransaction.setExtendedInfor hjubh)r }r (h}r (h]r h]r h]r hXmethodr jh]r h]r jj jXpyr uh hhNh jh]r (h)r }r (h}r (hXITransaction.setExtendedInfor hhh]r hMah]r h]r hjAh]r hMah]r huh hhKh hh]r (h)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh hh]r hXsetExtendedInfor r }r (hj h Uubah XsetExtendedInfor hjhj ubh)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh hh]r (j)r }r (hj h jh]r hXnamer r }r (hj h Uubah Xnamer h}r (h]r h]r h]r h]r h]r uubj)r }r (hj h jh]r hXvaluer r }r (hj h Uubah Xvaluer h}r (h]r h]r h]r h]r h]r uubeh Uhjhj ubh)r }r (h}r (h]r h]r h]r h]r h]r Uexprr! huh hhNh hh]r" h)r# }r$ (hj h hh]r% h)r& }r' (hj# h hh]r( hX[source]r) r* }r+ (hj& h Uubah Uh}r, (h]r- hah]r. h]r/ h]r0 h]r1 uubah Uh}r2 (Urefdocr3 hUrefidr4 j h]r5 h]r6 U refexplicitr7 Ureftyper8 hh]r9 h]r: U refdomainr; hh]r< U reftargetr= X_modules/transaction/interfacesr> uubah UhNhj ubeh XsetExtendedInfo(name, value)r? hjhj ubj )r@ }rA (h}rB (h]rC h]rD h]rE h]rF h]rG uh hhKh jh]rH (j)rI }rJ (h}rK (h]rL h]rM h]rN h]rO h]rP uh hhKh jhj h X&Add extension data to the transaction.rQ h]rR hX&Add extension data to the transaction.rS rT }rU (hjI h jQ ubahj@ ubj)rV }rW (h}rX (h]rY h]rZ h]r[ h]r\ h]r] uh hhKh jhj h Xname is the name of the extension property to set, of Python type str; value must be picklable. Multiple calls may be made to set multiple extension properties, provided the names are distinct.r^ h]r_ hXname is the name of the extension property to set, of Python type str; value must be picklable. Multiple calls may be made to set multiple extension properties, provided the names are distinct.r` ra }rb (hjV h j^ ubahj@ ubj)rc }rd (h}re (h]rf h]rg h]rh h]ri h]rj uh hhKh jhj h XMStorages record the extension data, as meta-data, when a transaction commits.rk h]rl hXMStorages record the extension data, as meta-data, when a transaction commits.rm rn }ro (hjc h jk ubahj@ ubj)rp }rq (h}rr (h]rs h]rt h]ru h]rv h]rw uh hhK h jhj h XA storage may impose a limit on the size of extension data; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or remove `` pairs).rx h]ry (hXA storage may impose a limit on the size of extension data; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or remove rz r{ }r| (hjp h XA storage may impose a limit on the size of extension data; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or remove r} ubj4 )r~ }r (hjp h j7 h]r hX r r }r (hj~ h Uubah X``r h}r (h]r h]r h]r h]r h]r uubhX pairs).r r }r (hjp h X pairs).r ubehj@ ubeh Uhjhj ubeh Uhj hjubj`)r }r (h}r (h]r h]r h]r Uentriesr ]r (jiX?beforeCommitHook() (transaction.interfaces.ITransaction method)h\Utr ah]r h]r uh hhNh jmh]r h UhXAdocstring of transaction.interfaces.ITransaction.beforeCommitHookr hjubh)r }r (h}r (h]r h]r h]r hXmethodr jh]r h]r jj jXpyr uh hhNh jh]r (h)r }r (h}r (hXITransaction.beforeCommitHookr hhh]r h\ah]r h]r hjAh]r h\ah]r huh hhKh hh]r (h)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh hh]r hXbeforeCommitHookr r }r (hj h Uubah XbeforeCommitHookr hjhj ubh)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh hh]r (j)r }r (hj h jh]r hX_ITransaction__hookr r }r (hj h Uubah X_ITransaction__hookr h}r (h]r h]r h]r h]r h]r uubj)r }r (hj h jh]r hX*argsr r }r (hj h Uubah X*argsr h}r (h]r h]r h]r h]r h]r uubj)r }r (hj h jh]r hX**kwsr r }r (hj h Uubah X**kwsr h}r (h]r h]r h]r h]r h]r uubeh Uhjhj ubh)r }r (h}r (h]r h]r h]r h]r h]r Uexprr huh hhNh hh]r h)r }r (hj h hh]r h)r }r (hj h hh]r hX[source]r r }r(hj h Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidr j h]r h]r U refexplicitr Ureftyper hh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhj ubeh X3beforeCommitHook(_ITransaction__hook, *args, **kws)rhjhj ubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r (h]r!h]r"h]r#h]r$h]r%uh hhKh jhj h X<Register a hook to call before the transaction is committed.r&h]r'hX<Register a hook to call before the transaction is committed.r(r)}r*(hjh j&ubahjubj)r+}r,(h}r-(h]r.h]r/h]r0h]r1h]r2uh hhKh jhj h XCTHIS IS DEPRECATED IN ZODB 3.6. Use addBeforeCommitHook() instead.r3h]r4hXCTHIS IS DEPRECATED IN ZODB 3.6. Use addBeforeCommitHook() instead.r5r6}r7(hj+h j3ubahjubj)r8}r9(h}r:(h]r;h]r<h]r=h]r>h]r?uh hhKh jhj h XThe specified hook function will be called after the transaction's commit method has been called, but before the commit process has been started. The hook will be passed the specified positional and keyword arguments.r@h]rAhXThe specified hook function will be called after the transaction's commit method has been called, but before the commit process has been started. The hook will be passed the specified positional and keyword arguments.rBrC}rD(hj8h j@ubahjubj)rE}rF(h}rG(h]rHh]rIh]rJh]rKh]rLuh hhK h jhj h X2Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks.rMh]rNhX2Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks.rOrP}rQ(hjEh jMubahjubj)rR}rS(h}rT(h]rUh]rVh]rWh]rXh]rYuh hhKh jhj h XHooks are called only for a top-level commit. A savepoint does not call any hooks. If the transaction is aborted, hooks are not called, and are discarded. Calling a hook "consumes" its registration too: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then beforeCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead.rZh]r[hXHooks are called only for a top-level commit. A savepoint does not call any hooks. If the transaction is aborted, hooks are not called, and are discarded. Calling a hook "consumes" its registration too: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then beforeCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead.r\r]}r^(hjRh jZubahjubeh Uhjhj ubeh Uhj hjubj`)r_}r`(h}ra(h]rbh]rch]rdUentriesre]rf(jiXBaddBeforeCommitHook() (transaction.interfaces.ITransaction method)hUtrgah]rhh]riuh hhNh jmh]rjh UhXDdocstring of transaction.interfaces.ITransaction.addBeforeCommitHookrkhjubh)rl}rm(h}rn(h]roh]rph]rqhXmethodrrjh]rsh]rtjjrjXpyruuh hhNh jh]rv(h)rw}rx(h}ry(hX ITransaction.addBeforeCommitHookrzhhh]r{hah]r|h]r}hjAh]r~hah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXaddBeforeCommitHookrr}r(hjh Uubah XaddBeforeCommitHookrhjhjwubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]r(j)r}r(hjh jh]rhXhookrr}r(hjh Uubah Xhookrh}r(h]rh]rh]rh]rh]ruubj)r}r(hjh jh]rhXargs=()rr}r(hjh Uubah Xargs=()rh}r(h]rh]rh]rh]rh]ruubj)r}r(hjh jh]rhXkws=Nonerr}r(hjh Uubah Xkws=Nonerh}r(h]rh]rh]rh]rh]ruubeh Uhjhjwubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjzh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjwubeh X,addBeforeCommitHook(hook, args=(), kws=None)rhjhjlubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjkh X<Register a hook to call before the transaction is committed.rh]rhX<Register a hook to call before the transaction is committed.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjkh XThe specified hook function will be called after the transaction's commit method has been called, but before the commit process has been started. The hook will be passed the specified positional (`args`) and keyword (`kws`) arguments. `args` is a sequence of positional arguments to be passed, defaulting to an empty tuple (no positional arguments are passed). `kws` is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed).rh]r(hXThe specified hook function will be called after the transaction's commit method has been called, but before the commit process has been started. The hook will be passed the specified positional (rr}r(hjh XThe specified hook function will be called after the transaction's commit method has been called, but before the commit process has been started. The hook will be passed the specified positional (rubj4 )r }r (hjh j7 h]r hXargsr r }r(hj h Uubah X`args`rh}r(h]rh]rh]rh]rh]ruubhX) and keyword (rr}r(hjh X) and keyword (rubj4 )r}r(hjh j7 h]rhXkwsrr}r(hjh Uubah X`kws`r h}r!(h]r"h]r#h]r$h]r%h]r&uubhX) arguments. r'r(}r)(hjh X) arguments. r*ubj4 )r+}r,(hjh j7 h]r-hXargsr.r/}r0(hj+h Uubah X`args`r1h}r2(h]r3h]r4h]r5h]r6h]r7uubhXy is a sequence of positional arguments to be passed, defaulting to an empty tuple (no positional arguments are passed). r8r9}r:(hjh Xy is a sequence of positional arguments to be passed, defaulting to an empty tuple (no positional arguments are passed). r;ubj4 )r<}r=(hjh j7 h]r>hXkwsr?r@}rA(hj<h Uubah X`kws`rBh}rC(h]rDh]rEh]rFh]rGh]rHuubhXz is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed).rIrJ}rK(hjh Xz is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed).rLubehjubj)rM}rN(h}rO(h]rPh]rQh]rRh]rSh]rTuh hhK h jhjkh X2Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks.rUh]rVhX2Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks.rWrX}rY(hjMh jUubahjubj)rZ}r[(h}r\(h]r]h]r^h]r_h]r`h]rauh hhKh jhjkh XHooks are called only for a top-level commit. A savepoint creation does not call any hooks. If the transaction is aborted, hooks are not called, and are discarded. Calling a hook "consumes" its registration too: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then addBeforeCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead.rbh]rchXHooks are called only for a top-level commit. A savepoint creation does not call any hooks. If the transaction is aborted, hooks are not called, and are discarded. Calling a hook "consumes" its registration too: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then addBeforeCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead.rdre}rf(hjZh jbubahjubeh Uhjhjlubeh Uhjkhjubj`)rg}rh(h}ri(h]rjh]rkh]rlUentriesrm]rn(jiXCgetBeforeCommitHooks() (transaction.interfaces.ITransaction method)hUtroah]rph]rquh hhNh jmh]rrh UhXEdocstring of transaction.interfaces.ITransaction.getBeforeCommitHooksrshjubh)rt}ru(h}rv(h]rwh]rxh]ryhXmethodrzjh]r{h]r|jjzjXpyr}uh hhNh jh]r~(h)r}r(h}r(hX!ITransaction.getBeforeCommitHooksrhhh]rhah]rh]rhjAh]rhah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXgetBeforeCommitHooksrr}r(hjh Uubah XgetBeforeCommitHooksrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rh Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh XgetBeforeCommitHooks()rhjhjtubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjsh X?Return iterable producing the registered addBeforeCommit hooks.rh]rhX?Return iterable producing the registered addBeforeCommit hooks.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjsh XA triple (hook, args, kws) is produced for each registered hook. The hooks are produced in the order in which they would be invoked by a top-level transaction commit.rh]rhXA triple (hook, args, kws) is produced for each registered hook. The hooks are produced in the order in which they would be invoked by a top-level transaction commit.rr}r(hjh jubahjubeh Uhjhjtubeh Uhjshjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiXAaddAfterCommitHook() (transaction.interfaces.ITransaction method)hUtrah]rh]ruh hhNh jmh]rh UhXCdocstring of transaction.interfaces.ITransaction.addAfterCommitHookrhjubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXITransaction.addAfterCommitHookrhhh]rhah]rh]rhjAh]rhah]r huh hhKh hh]r (h)r }r (h}r (h]rh]rh]rh]rh]ruh hhKh hh]rhXaddAfterCommitHookrr}r(hj h Uubah XaddAfterCommitHookrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]r (j)r!}r"(hjh jh]r#hXhookr$r%}r&(hj!h Uubah Xhookr'h}r((h]r)h]r*h]r+h]r,h]r-uubj)r.}r/(hjh jh]r0hXargs=()r1r2}r3(hj.h Uubah Xargs=()r4h}r5(h]r6h]r7h]r8h]r9h]r:uubj)r;}r<(hjh jh]r=hXkws=Noner>r?}r@(hj;h Uubah Xkws=NonerAh}rB(h]rCh]rDh]rEh]rFh]rGuubeh Uhjhjubh)rH}rI(h}rJ(h]rKh]rLh]rMh]rNh]rOUexprrPhuh hhNh hh]rQh)rR}rS(hjHh hh]rTh)rU}rV(hjRh hh]rWhX[source]rXrY}rZ(hjUh Uubah Uh}r[(h]r\hah]r]h]r^h]r_h]r`uubah Uh}ra(UrefdocrbhUrefidrcjh]rdh]reU refexplicitrfUreftyperghh]rhh]riU refdomainrjhh]rkU reftargetrlX_modules/transaction/interfacesrmuubah UhNhjubeh X+addAfterCommitHook(hook, args=(), kws=None)rnhjhjubj )ro}rp(h}rq(h]rrh]rsh]rth]ruh]rvuh hhKh jh]rw(j)rx}ry(h}rz(h]r{h]r|h]r}h]r~h]ruh hhKh jhjh X;Register a hook to call after a transaction commit attempt.rh]rhX;Register a hook to call after a transaction commit attempt.rr}r(hjxh jubahjoubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh X;The specified hook function will be called after the transaction commit succeeds or aborts. The first argument passed to the hook is a Boolean value, true if the commit succeeded, or false if the commit aborted. `args` specifies additional positional, and `kws` keyword, arguments to pass to the hook. `args` is a sequence of positional arguments to be passed, defaulting to an empty tuple (only the true/false success argument is passed). `kws` is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed).rh]r(hXThe specified hook function will be called after the transaction commit succeeds or aborts. The first argument passed to the hook is a Boolean value, true if the commit succeeded, or false if the commit aborted. rr}r(hjh XThe specified hook function will be called after the transaction commit succeeds or aborts. The first argument passed to the hook is a Boolean value, true if the commit succeeded, or false if the commit aborted. rubj4 )r}r(hjh j7 h]rhXargsrr}r(hjh Uubah X`args`rh}r(h]rh]rh]rh]rh]ruubhX& specifies additional positional, and rr}r(hjh X& specifies additional positional, and rubj4 )r}r(hjh j7 h]rhXkwsrr}r(hjh Uubah X`kws`rh}r(h]rh]rh]rh]rh]ruubhX* keyword, arguments to pass to the hook. rr}r(hjh X* keyword, arguments to pass to the hook. rubj4 )r}r(hjh j7 h]rhXargsrr}r(hjh Uubah X`args`rh}r(h]rh]rh]rh]rh]ruubhX is a sequence of positional arguments to be passed, defaulting to an empty tuple (only the true/false success argument is passed). rr}r(hjh X is a sequence of positional arguments to be passed, defaulting to an empty tuple (only the true/false success argument is passed). rubj4 )r}r(hjh j7 h]rhXkwsrr}r(hjh Uubah X`kws`rh}r(h]rh]rh]rh]rh]ruubhXz is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed).rr}r(hjh Xz is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed).rubehjoubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhK h jhjh X2Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks.rh]rhX2Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks.rr}r(hjh jubahjoubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XHooks are called only for a top-level commit. A savepoint creation does not call any hooks. Calling a hook "consumes" its registration: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then addAfterCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead.rh]rhXHooks are called only for a top-level commit. A savepoint creation does not call any hooks. Calling a hook "consumes" its registration: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then addAfterCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead.rr}r(hjh jubahjoubeh Uhjhjubeh Uhjhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiXBgetAfterCommitHooks() (transaction.interfaces.ITransaction method)h9Utrah]rh]ruh hhNh jmh]rh UhXDdocstring of transaction.interfaces.ITransaction.getAfterCommitHooksrhjubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r }r (h}r (hX ITransaction.getAfterCommitHooksr hhh]r h9ah]rh]rhjAh]rh9ah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXgetAfterCommitHooksrr}r(hjh Uubah XgetAfterCommitHooksrhjhj ubh)r }r!(h}r"(h]r#h]r$h]r%h]r&h]r'uh hhKh hh]r(h Uhjhj ubh)r)}r*(h}r+(h]r,h]r-h]r.h]r/h]r0Uexprr1huh hhNh hh]r2h)r3}r4(hj)h hh]r5h)r6}r7(hj3h hh]r8hX[source]r9r:}r;(hj6h Uubah Uh}r<(h]r=hah]r>h]r?h]r@h]rAuubah Uh}rB(UrefdocrChUrefidrDj h]rEh]rFU refexplicitrGUreftyperHhh]rIh]rJU refdomainrKhh]rLU reftargetrMX_modules/transaction/interfacesrNuubah UhNhj ubeh XgetAfterCommitHooks()rOhjhjubj )rP}rQ(h}rR(h]rSh]rTh]rUh]rVh]rWuh hhKh jh]rX(j)rY}rZ(h}r[(h]r\h]r]h]r^h]r_h]r`uh hhKh jhjh X>Return iterable producing the registered addAfterCommit hooks.rah]rbhX>Return iterable producing the registered addAfterCommit hooks.rcrd}re(hjYh jaubahjPubj)rf}rg(h}rh(h]rih]rjh]rkh]rlh]rmuh hhKh jhjh XA triple (hook, args, kws) is produced for each registered hook. The hooks are produced in the order in which they would be invoked by a top-level transaction commit.rnh]rohXA triple (hook, args, kws) is produced for each registered hook. The hooks are produced in the order in which they would be invoked by a top-level transaction commit.rprq}rr(hjfh jnubahjPubeh Uhjhjubeh Uhjhjubeh Uhjohj3ubeh UhNhjubj`)rs}rt(h}ru(h]rvh]rwh]rxUentriesry]rz(jiX2IDataManager (interface in transaction.interfaces)haUtr{ah]r|h]r}uh hhNh jmh]r~h UhNhjubh)r}r(h}r(h]rh]rh]rhX interfacerjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hX IDataManagerrhhXtransaction.interfacesrr}rbh]rhaah]rh]rhUh]rhaah]rhuh hhNh hh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhX interface rr}r(hjh Uubah X interface rhjohjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhXtransaction.interfaces.rr}r(hjh Uubah Xtransaction.interfaces.rhjohjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh hh]rhX IDataManagerrr}r(hjh Uubah jhjohjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh jhjohjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.IDataManagerrh X*Objects that manage transactional storage.rh]rhX*Objects that manage transactional storage.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XThese objects may manage data for other objects, or they may manage non-object storages, such as relational databases. For example, a ZODB.Connection.rh]rhXThese objects may manage data for other objects, or they may manage non-object storages, such as relational databases. For example, a ZODB.Connection.rr}r(hjh jubahjubj)r}r(h}r (h]r h]r h]r h]r h]ruh hhKh jhjh XNote that when some data is modified, that data's data manager should join a transaction so that data can be committed when the user commits the transaction.rh]rhXNote that when some data is modified, that data's data manager should join a transaction so that data can be committed when the user commits the transaction.rr}r(hjh jubahjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiXCtransaction_manager (transaction.interfaces.IDataManager attribute)hHUtrah]rh]ruh hhNh jmh]rh UhXDdocstring of transaction.interfaces.IDataManager.transaction_managerr hjubh)r!}r"(h}r#(h]r$h]r%h]r&hX attributer'jh]r(h]r)jj'jXpyr*uh hhNh jh]r+(h)r,}r-(h}r.(hX IDataManager.transaction_managerr/hhh]r0hHah]r1h]r2hjh]r3hHah]r4huh hhKh hh]r5h)r6}r7(h}r8(h]r9h]r:h]r;h]r<h]r=uh hhKh hh]r>hXtransaction_managerr?r@}rA(hj6h Uubah Xtransaction_managerrBhjhj,ubah jBhjhj!ubj )rC}rD(h}rE(h]rFh]rGh]rHh]rIh]rJuh hhKh jh]rK(j)rL}rM(h}rN(h]rOh]rPh]rQh]rRh]rSuh hhKh jhj h X7The transaction manager (TM) used by this data manager.rTh]rUhX7The transaction manager (TM) used by this data manager.rVrW}rX(hjLh jTubahjCubj)rY}rZ(h}r[(h]r\h]r]h]r^h]r_h]r`uh hhKh jhj h XThis is a public attribute, intended for read-only use. The value is an instance of ITransactionManager, typically set by the data manager's constructor.rah]rbhXThis is a public attribute, intended for read-only use. The value is an instance of ITransactionManager, typically set by the data manager's constructor.rcrd}re(hjYh jaubahjCubeh Uhjhj!ubeh Uhj hjubj`)rf}rg(h}rh(h]rih]rjh]rkUentriesrl]rm(jiX4abort() (transaction.interfaces.IDataManager method)h7Utrnah]roh]rpuh hhNh jmh]rqh UhX6docstring of transaction.interfaces.IDataManager.abortrrhjubh)rs}rt(h}ru(h]rvh]rwh]rxhXmethodryjh]rzh]r{jjyjXpyr|uh hhNh jh]r}(h)r~}r(h}r(hXIDataManager.abortrhhh]rh7ah]rh]rhjh]rh7ah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXabortrr}r(hjh Uubah Xabortrhjhj~ubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rj)r}r(hjh jh]rhX transactionrr}r(hjh Uubah X transactionrh}r(h]rh]rh]rh]rh]ruubah Uhjhj~ubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhj~ubeh Xabort(transaction)rhjhjsubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjrh X+Abort a transaction and forget all changes.rh]rhX+Abort a transaction and forget all changes.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjrh X3Abort must be called outside of a two-phase commit.rh]rhX3Abort must be called outside of a two-phase commit.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjrh XAbort is called by the transaction manager to abort transactions that are not yet in a two-phase commit. It may also be called when rolling back a savepoint made before the data manager joined the transaction.rh]rhXAbort is called by the transaction manager to abort transactions that are not yet in a two-phase commit. It may also be called when rolling back a savepoint made before the data manager joined the transaction.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]r uh hhK h jhjrh XIn any case, after abort is called, the data manager is no longer participating in the transaction. If there are new changes, the data manager must rejoin the transaction.r h]r hXIn any case, after abort is called, the data manager is no longer participating in the transaction. If there are new changes, the data manager must rejoin the transaction.r r }r(hjh j ubahjubeh Uhjhjsubeh Uhjrhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX8tpc_begin() (transaction.interfaces.IDataManager method)h)Utrah]rh]ruh hhNh jmh]rh UhX:docstring of transaction.interfaces.IDataManager.tpc_beginrhjubh)r}r(h}r(h]rh]r h]r!hXmethodr"jh]r#h]r$jj"jXpyr%uh hhNh jh]r&(h)r'}r((h}r)(hXIDataManager.tpc_beginr*hhh]r+h)ah]r,h]r-hjh]r.h)ah]r/huh hhKh hh]r0(h)r1}r2(h}r3(h]r4h]r5h]r6h]r7h]r8uh hhKh hh]r9hX tpc_beginr:r;}r<(hj1h Uubah X tpc_beginr=hjhj'ubh)r>}r?(h}r@(h]rAh]rBh]rCh]rDh]rEuh hhKh hh]rFj)rG}rH(hj>h jh]rIhX transactionrJrK}rL(hjGh Uubah X transactionrMh}rN(h]rOh]rPh]rQh]rRh]rSuubah Uhjhj'ubh)rT}rU(h}rV(h]rWh]rXh]rYh]rZh]r[Uexprr\huh hhNh hh]r]h)r^}r_(hjTh hh]r`h)ra}rb(hj^h hh]rchX[source]rdre}rf(hjah Uubah Uh}rg(h]rhhah]rih]rjh]rkh]rluubah Uh}rm(UrefdocrnhUrefidroj*h]rph]rqU refexplicitrrUreftypershh]rth]ruU refdomainrvhh]rwU reftargetrxX_modules/transaction/interfacesryuubah UhNhj'ubeh Xtpc_begin(transaction)rzhjhjubj )r{}r|(h}r}(h]r~h]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh X=Begin commit of a transaction, starting the two-phase commit.rh]rhX=Begin commit of a transaction, starting the two-phase commit.rr}r(hjh jubahj{ubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XYtransaction is the ITransaction instance associated with the transaction being committed.rh]rhXYtransaction is the ITransaction instance associated with the transaction being committed.rr}r(hjh jubahj{ubeh Uhjhjubeh Uhjhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX5commit() (transaction.interfaces.IDataManager method)h,Utrah]rh]ruh hhNh jmh]rh UhX7docstring of transaction.interfaces.IDataManager.commitrhjubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXIDataManager.commitrhhh]rh,ah]rh]rhjh]rh,ah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXcommitrr}r(hjh Uubah Xcommitrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rj)r}r(hjh jh]rhX transactionrr}r(hjh Uubah X transactionrh}r(h]rh]rh]rh]rh]ruubah Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh Xcommit(transaction)r hjhjubj )r }r (h}r (h]r h]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh X+Commit modifications to registered objects.rh]rhX+Commit modifications to registered objects.rr}r(hjh jubahj ubj)r }r!(h}r"(h]r#h]r$h]r%h]r&h]r'uh hhKh jhjh XSave changes to be made persistent if the transaction commits (if tpc_finish is called later). If tpc_abort is called later, changes must not persist.r(h]r)hXSave changes to be made persistent if the transaction commits (if tpc_finish is called later). If tpc_abort is called later, changes must not persist.r*r+}r,(hj h j(ubahj ubj)r-}r.(h}r/(h]r0h]r1h]r2h]r3h]r4uh hhKh jhjh XThis includes conflict detection and handling. If no conflicts or errors occur, the data manager should be prepared to make the changes persist when tpc_finish is called.r5h]r6hXThis includes conflict detection and handling. If no conflicts or errors occur, the data manager should be prepared to make the changes persist when tpc_finish is called.r7r8}r9(hj-h j5ubahj ubeh Uhjhjubeh Uhjhjubj`)r:}r;(h}r<(h]r=h]r>h]r?Uentriesr@]rA(jiX7tpc_vote() (transaction.interfaces.IDataManager method)hUtrBah]rCh]rDuh hhNh jmh]rEh UhX9docstring of transaction.interfaces.IDataManager.tpc_voterFhjubh)rG}rH(h}rI(h]rJh]rKh]rLhXmethodrMjh]rNh]rOjjMjXpyrPuh hhNh jh]rQ(h)rR}rS(h}rT(hXIDataManager.tpc_voterUhhh]rVhah]rWh]rXhjh]rYhah]rZhuh hhKh hh]r[(h)r\}r](h}r^(h]r_h]r`h]rah]rbh]rcuh hhKh hh]rdhXtpc_votererf}rg(hj\h Uubah Xtpc_voterhhjhjRubh)ri}rj(h}rk(h]rlh]rmh]rnh]roh]rpuh hhKh hh]rqj)rr}rs(hjih jh]rthX transactionrurv}rw(hjrh Uubah X transactionrxh}ry(h]rzh]r{h]r|h]r}h]r~uubah UhjhjRubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjUh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjRubeh Xtpc_vote(transaction)rhjhjGubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjFh X6Verify that a data manager can commit the transaction.rh]rhX6Verify that a data manager can commit the transaction.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjFh XlThis is the last chance for a data manager to vote 'no'. A data manager votes 'no' by raising an exception.rh]rhXlThis is the last chance for a data manager to vote 'no'. A data manager votes 'no' by raising an exception.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjFh XYtransaction is the ITransaction instance associated with the transaction being committed.rh]rhXYtransaction is the ITransaction instance associated with the transaction being committed.rr}r(hjh jubahjubeh UhjhjGubeh UhjFhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX9tpc_finish() (transaction.interfaces.IDataManager method)h/Utrah]rh]ruh hhNh jmh]rh UhX;docstring of transaction.interfaces.IDataManager.tpc_finishrhjubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXIDataManager.tpc_finishrhhh]rh/ah]rh]rhjh]rh/ah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhX tpc_finishrr}r(hjh Uubah X tpc_finishrhjhjubh)r}r(h}r(h]rh]r h]r h]r h]r uh hhKh hh]r j)r}r(hjh jh]rhX transactionrr}r(hjh Uubah X transactionrh}r(h]rh]rh]rh]rh]ruubah Uhjhjubh)r}r(h}r(h]rh]rh]r h]r!h]r"Uexprr#huh hhNh hh]r$h)r%}r&(hjh hh]r'h)r(}r)(hj%h hh]r*hX[source]r+r,}r-(hj(h Uubah Uh}r.(h]r/hah]r0h]r1h]r2h]r3uubah Uh}r4(Urefdocr5hUrefidr6jh]r7h]r8U refexplicitr9Ureftyper:hh]r;h]r<U refdomainr=hh]r>U reftargetr?X_modules/transaction/interfacesr@uubah UhNhjubeh Xtpc_finish(transaction)rAhjhjubj )rB}rC(h}rD(h]rEh]rFh]rGh]rHh]rIuh hhKh jh]rJ(j)rK}rL(h}rM(h]rNh]rOh]rPh]rQh]rRuh hhKh jhjh X3Indicate confirmation that the transaction is done.rSh]rThX3Indicate confirmation that the transaction is done.rUrV}rW(hjKh jSubahjBubj)rX}rY(h}rZ(h]r[h]r\h]r]h]r^h]r_uh hhKh jhjh XAMake all changes to objects modified by this transaction persist.r`h]rahXAMake all changes to objects modified by this transaction persist.rbrc}rd(hjXh j`ubahjBubj)re}rf(h}rg(h]rhh]rih]rjh]rkh]rluh hhKh jhjh XYtransaction is the ITransaction instance associated with the transaction being committed.rmh]rnhXYtransaction is the ITransaction instance associated with the transaction being committed.rorp}rq(hjeh jmubahjBubj)rr}rs(h}rt(h]ruh]rvh]rwh]rxh]ryuh hhKh jhjh XThis should never fail. If this raises an exception, the database is not expected to maintain consistency; it's a serious error.rzh]r{hXThis should never fail. If this raises an exception, the database is not expected to maintain consistency; it's a serious error.r|r}}r~(hjrh jzubahjBubeh Uhjhjubeh Uhjhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX8tpc_abort() (transaction.interfaces.IDataManager method)hFUtrah]rh]ruh hhNh jmh]rh UhX:docstring of transaction.interfaces.IDataManager.tpc_abortrhjubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXIDataManager.tpc_abortrhhh]rhFah]rh]rhjh]rhFah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhX tpc_abortrr}r(hjh Uubah X tpc_abortrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rj)r}r(hjh jh]rhX transactionrr}r(hjh Uubah X transactionrh}r(h]rh]rh]rh]rh]ruubah Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh Xtpc_abort(transaction)rhjhjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XAbort a transaction.rh]rhXAbort a transaction.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XThis is called by a transaction manager to end a two-phase commit on the data manager. Abandon all changes to objects modified by this transaction.r h]r hXThis is called by a transaction manager to end a two-phase commit on the data manager. Abandon all changes to objects modified by this transaction.r r }r (hjh j ubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XYtransaction is the ITransaction instance associated with the transaction being committed.rh]rhXYtransaction is the ITransaction instance associated with the transaction being committed.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]r h]r!h]r"uh hhK h jhjh XThis should never fail.r#h]r$hXThis should never fail.r%r&}r'(hjh j#ubahjubeh Uhjhjubeh Uhjhjubj`)r(}r)(h}r*(h]r+h]r,h]r-Uentriesr.]r/(jiX6sortKey() (transaction.interfaces.IDataManager method)hKUtr0ah]r1h]r2uh hhNh jmh]r3h UhX8docstring of transaction.interfaces.IDataManager.sortKeyr4hjubh)r5}r6(h}r7(h]r8h]r9h]r:hXmethodr;jh]r<h]r=jj;jXpyr>uh hhNh jh]r?(h)r@}rA(h}rB(hXIDataManager.sortKeyrChhh]rDhKah]rEh]rFhjh]rGhKah]rHhuh hhKh hh]rI(h)rJ}rK(h}rL(h]rMh]rNh]rOh]rPh]rQuh hhKh hh]rRhXsortKeyrSrT}rU(hjJh Uubah XsortKeyrVhj4hj@ubh)rW}rX(h}rY(h]rZh]r[h]r\h]r]h]r^uh hhKh hh]r_h Uhj4hj@ubh)r`}ra(h}rb(h]rch]rdh]reh]rfh]rgUexprrhhuh hhNh hh]rih)rj}rk(hj`h hh]rlh)rm}rn(hjjh hh]rohX[source]rprq}rr(hjmh Uubah Uh}rs(h]rthah]ruh]rvh]rwh]rxuubah Uh}ry(UrefdocrzhUrefidr{jCh]r|h]r}U refexplicitr~Ureftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhj@ubeh X sortKey()rhj4hj5ubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhj4h X9Return a key to use for ordering registered DataManagers.rh]rhX9Return a key to use for ordering registered DataManagers.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhj4h X=In order to guarantee a total ordering, keys must be strings.rh]rhX=In order to guarantee a total ordering, keys must be strings.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhj4h XZODB uses a global sort order to prevent deadlock when it commits transactions involving multiple resource managers. The resource manager must define a sortKey() method that provides a global ordering for resource managers.rh]rhXZODB uses a global sort order to prevent deadlock when it commits transactions involving multiple resource managers. The resource manager must define a sortKey() method that provides a global ordering for resource managers.rr}r(hjh jubahjubeh Uhj4hj5ubeh Uhj4hjubeh Uhjohjubeh UhNhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX;ISavepointDataManager (interface in transaction.interfaces)hUtrah]rh]ruh hhNh jmh]rh UhNhjubjpj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX;IDataManagerSavepoint (interface in transaction.interfaces)hBUtrah]rh]ruh hhNh jmh]rh UhNhjubh)r}r(h}r(h]rh]rh]rhX interfacerjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXIDataManagerSavepointrhhXtransaction.interfacesrr}rbh]rhBah]rh]rhUh]rhBah]rhuh hhNh hh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhX interface rr}r(hjh Uubah X interface rhjohjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhXtransaction.interfaces.rr}r(hjh Uubah Xtransaction.interfaces.rhjohjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh hh]r hXIDataManagerSavepointr r }r (hjh Uubah jhjohjubh)r }r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hj h hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r (h]r!hah]r"h]r#h]r$h]r%uubah Uh}r&(Urefdocr'hUrefidr(jh]r)h]r*U refexplicitr+Ureftyper,hh]r-h]r.U refdomainr/hh]r0U reftargetr1X_modules/transaction/interfacesr2uubah UhNhjubeh jhjohjubj )r3}r4(h}r5(h]r6h]r7h]r8h]r9h]r:uh hhNh jh]r;(j)r<}r=(h}r>(h]r?h]r@h]rAh]rBh]rCuh hhKh jhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.IDataManagerSavepointrDh XESavepoint for data-manager changes for use in transaction savepoints.rEh]rFhXESavepoint for data-manager changes for use in transaction savepoints.rGrH}rI(hj<h jEubahj3ubj)rJ}rK(h}rL(h]rMh]rNh]rOh]rPh]rQuh hhKh jhjDh XHDatamanager savepoints are used by, and only by, transaction savepoints.rRh]rShXHDatamanager savepoints are used by, and only by, transaction savepoints.rTrU}rV(hjJh jRubahj3ubj)rW}rX(h}rY(h]rZh]r[h]r\h]r]h]r^uh hhKh jhjDh XqNote that data manager savepoints don't have any notion of, or responsibility for, validity. It isn't the responsibility of data-manager savepoints to prevent multiple rollbacks or rollbacks after transaction termination. Preventing invalid savepoint rollback is the responsibility of transaction rollbacks. Application code should never use data-manager savepoints.r_h]r`hXqNote that data manager savepoints don't have any notion of, or responsibility for, validity. It isn't the responsibility of data-manager savepoints to prevent multiple rollbacks or rollbacks after transaction termination. Preventing invalid savepoint rollback is the responsibility of transaction rollbacks. Application code should never use data-manager savepoints.rarb}rc(hjWh j_ubahj3ubj`)rd}re(h}rf(h]rgh]rhh]riUentriesrj]rk(jiX@rollback() (transaction.interfaces.IDataManagerSavepoint method)hOUtrlah]rmh]rnuh hhNh jmh]roh UhXBdocstring of transaction.interfaces.IDataManagerSavepoint.rollbackrphj3ubh)rq}rr(h}rs(h]rth]ruh]rvhXmethodrwjh]rxh]ryjjwjXpyrzuh hhNh jh]r{(h)r|}r}(h}r~(hXIDataManagerSavepoint.rollbackrhhh]rhOah]rh]rhjh]rhOah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXrollbackrr}r(hjh Uubah Xrollbackrhjphj|ubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rh Uhjphj|ubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhj|ubeh X rollback()rhjphjqubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]rj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjph X+Rollback any work done since the savepoint.rh]rhX+Rollback any work done since the savepoint.rr}r(hjh jubahjubah Uhjphjqubeh Uhjphj3ubeh Uhjohjubeh UhNhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX0ISavepoint (interface in transaction.interfaces)hUtrah]rh]ruh hhNh jmh]rh UhNhjubh)r}r(h}r(h]rh]rh]rhX interfacerjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hX ISavepointrhhXtransaction.interfacesrr}rbh]rhah]rh]rhUh]rhah]rhuh hhNh hh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhX interface rr}r(hjh Uubah X interface r hjohjubj)r }r (h}r (h]r h]rh]rh]rh]ruh hhNh jh]rhXtransaction.interfaces.rr}r(hj h Uubah Xtransaction.interfaces.rhjohjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh hh]rhX ISavepointr r!}r"(hjh Uubah jhjohjubh)r#}r$(h}r%(h]r&h]r'h]r(h]r)h]r*Uexprr+huh hhNh hh]r,h)r-}r.(hj#h hh]r/h)r0}r1(hj-h hh]r2hX[source]r3r4}r5(hj0h Uubah Uh}r6(h]r7hah]r8h]r9h]r:h]r;uubah Uh}r<(Urefdocr=hUrefidr>jh]r?h]r@U refexplicitrAUreftyperBhh]rCh]rDU refdomainrEhh]rFU reftargetrGX_modules/transaction/interfacesrHuubah UhNhjubeh jhjohjubj )rI}rJ(h}rK(h]rLh]rMh]rNh]rOh]rPuh hhNh jh]rQ(j)rR}rS(h}rT(h]rUh]rVh]rWh]rXh]rYuh hhKh jhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.ISavepointrZh XA transaction savepoint.r[h]r\hXA transaction savepoint.r]r^}r_(hjRh j[ubahjIubj`)r`}ra(h}rb(h]rch]rdh]reUentriesrf]rg(jiX5rollback() (transaction.interfaces.ISavepoint method)h=Utrhah]rih]rjuh hhNh jmh]rkh UhX7docstring of transaction.interfaces.ISavepoint.rollbackrlhjIubh)rm}rn(h}ro(h]rph]rqh]rrhXmethodrsjh]rth]rujjsjXpyrvuh hhNh jh]rw(h)rx}ry(h}rz(hXISavepoint.rollbackr{hhh]r|h=ah]r}h]r~hjh]rh=ah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXrollbackrr}r(hjh Uubah Xrollbackrhjhjxubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rh Uhjhjxubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrj{h]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjxubeh X rollback()rhjhjmubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjlh X+Rollback any work done since the savepoint.rh]rhX+Rollback any work done since the savepoint.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjlh XEInvalidSavepointRollbackError is raised if the savepoint isn't valid.rh]rhXEInvalidSavepointRollbackError is raised if the savepoint isn't valid.rr}r(hjh jubahjubeh Uhjhjmubeh UhjlhjIubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX3valid (transaction.interfaces.ISavepoint attribute)hUtrah]rh]ruh hhNh jmh]rh UhX4docstring of transaction.interfaces.ISavepoint.validrhjIubh)r}r(h}r(h]rh]rh]rhX attributerjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXISavepoint.validrhhh]rhah]rh]rhjh]rhah]rhuh hhKh hh]rh)r}r(h}r(h]rh]rh]r h]r h]r uh hhKh hh]r hXvalidr r}r(hjh Uubah Xvalidrhjhjubah jhjhjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]rj)r}r(h}r(h]rh]rh]rh]r h]r!uh hhKh jhjh X1Boolean indicating whether the savepoint is validr"h]r#hX1Boolean indicating whether the savepoint is validr$r%}r&(hjh j"ubahjubah Uhjhjubeh UhjhjIubeh Uhjohjubeh UhNhjubj`)r'}r((h}r)(h]r*h]r+h]r,Uentriesr-]r.(jiX?InvalidSavepointRollbackError (class in transaction.interfaces)h]Utr/ah]r0h]r1uh hhNh jmh]r2h UhNhjubh)r3}r4(h}r5(h]r6h]r7h]r8hXclassr9jh]r:h]r;jj9jXpyr<uh hhNh jh]r=(h)r>}r?(h}r@(hXInvalidSavepointRollbackErrorrAhhXtransaction.interfacesrBrC}rDbh]rEh]ah]rFh]rGhUh]rHh]ah]rIhuh hhNh hh]rJ(j)rK}rL(h}rM(h]rNh]rOh]rPh]rQh]rRuh hhNh jh]rShXclass rTrU}rV(hjKh Uubah Xclass rWhjohj>ubj)rX}rY(h}rZ(h]r[h]r\h]r]h]r^h]r_uh hhNh jh]r`hXtransaction.interfaces.rarb}rc(hjXh Uubah Xtransaction.interfaces.rdhjohj>ubh)re}rf(h}rg(h]rhh]rih]rjh]rkh]rluh hhNh hh]rmhXInvalidSavepointRollbackErrorrnro}rp(hjeh Uubah jAhjohj>ubh)rq}rr(h}rs(h]rth]ruh]rvh]rwh]rxUexprryhuh hhNh hh]rzh)r{}r|(hjqh hh]r}h)r~}r(hj{h hh]rhX[source]rr}r(hj~h Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjAh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhj>ubeh jAhjohj3ubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.InvalidSavepointRollbackErrorrh X)Attempt to rollback an invalid savepoint.rh]rhX)Attempt to rollback an invalid savepoint.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh X#A savepoint may be invalid because:rh]rhX#A savepoint may be invalid because:rr}r(hjh jubahjubcdocutils.nodes bullet_list r)r}r(h}r(UbulletrX-h]rh]rh]rh]rh]ruh hhKh U bullet_listrh]r(cdocutils.nodes list_item r)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh U list_itemrh]rj)r}r(h}r(h]rh]rh]rh]rh]ruhKh jhjh X5The surrounding transaction has committed or aborted.rh]rhX5The surrounding transaction has committed or aborted.rr}r(hjh jubahjubah X6The surrounding transaction has committed or aborted. rhjhjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rj)r}r(h}r(h]rh]rh]rh]rh]ruhKh jhjh XBAn earlier savepoint in the same transaction has been rolled back.rh]rhXBAn earlier savepoint in the same transaction has been rolled back.rr}r(hjh jubahjubah jhjhjubeh Uhjhjubeh Uhjohj3ubeh UhNhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX3ISynchronizer (interface in transaction.interfaces)h"Utrah]rh]ruh hhNh jmh]rh UhNhjubh)r}r(h}r(h]rh]rh]rhX interfacerjh]r h]r jjjXpyr uh hhNh jh]r (h)r }r(h}r(hX ISynchronizerrhhXtransaction.interfacesrr}rbh]rh"ah]rh]rhUh]rh"ah]rhuh hhNh hh]r(j)r}r(h}r(h]rh]rh]rh]r h]r!uh hhNh jh]r"hX interface r#r$}r%(hjh Uubah X interface r&hjohj ubj)r'}r((h}r)(h]r*h]r+h]r,h]r-h]r.uh hhNh jh]r/hXtransaction.interfaces.r0r1}r2(hj'h Uubah Xtransaction.interfaces.r3hjohj ubh)r4}r5(h}r6(h]r7h]r8h]r9h]r:h]r;uh hhNh hh]r<hX ISynchronizerr=r>}r?(hj4h Uubah jhjohj ubh)r@}rA(h}rB(h]rCh]rDh]rEh]rFh]rGUexprrHhuh hhNh hh]rIh)rJ}rK(hj@h hh]rLh)rM}rN(hjJh hh]rOhX[source]rPrQ}rR(hjMh Uubah Uh}rS(h]rThah]rUh]rVh]rWh]rXuubah Uh}rY(UrefdocrZhUrefidr[jh]r\h]r]U refexplicitr^Ureftyper_hh]r`h]raU refdomainrbhh]rcU reftargetrdX_modules/transaction/interfacesreuubah UhNhj ubeh jhjohjubj )rf}rg(h}rh(h]rih]rjh]rkh]rlh]rmuh hhNh jh]rn(j)ro}rp(h}rq(h]rrh]rsh]rth]ruh]rvuh hhKh jhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.ISynchronizerrwh XFObjects that participate in the transaction-boundary notification API.rxh]ryhXFObjects that participate in the transaction-boundary notification API.rzr{}r|(hjoh jxubahjfubj`)r}}r~(h}r(h]rh]rh]rUentriesr]r(jiX@beforeCompletion() (transaction.interfaces.ISynchronizer method)h5Utrah]rh]ruh hhNh jmh]rh UhXBdocstring of transaction.interfaces.ISynchronizer.beforeCompletionrhjfubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXISynchronizer.beforeCompletionrhhh]rh5ah]rh]rhjh]rh5ah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXbeforeCompletionrr}r(hjh Uubah XbeforeCompletionrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rj)r}r(hjh jh]rhX transactionrr}r(hjh Uubah X transactionrh}r(h]rh]rh]rh]rh]ruubah Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh XbeforeCompletion(transaction)rhjhjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]rj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh X@Hook that is called by the transaction at the start of a commit.rh]rhX@Hook that is called by the transaction at the start of a commit.rr}r(hjh jubahjubah Uhjhjubeh Uhjhjfubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX?afterCompletion() (transaction.interfaces.ISynchronizer method)hUtrah]rh]r uh hhNh jmh]r h UhXAdocstring of transaction.interfaces.ISynchronizer.afterCompletionr hjfubh)r }r (h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXISynchronizer.afterCompletionrhhh]rhah]rh]rhjh]rhah]rhuh hhKh hh]r (h)r!}r"(h}r#(h]r$h]r%h]r&h]r'h]r(uh hhKh hh]r)hXafterCompletionr*r+}r,(hj!h Uubah XafterCompletionr-hjhjubh)r.}r/(h}r0(h]r1h]r2h]r3h]r4h]r5uh hhKh hh]r6j)r7}r8(hj.h jh]r9hX transactionr:r;}r<(hj7h Uubah X transactionr=h}r>(h]r?h]r@h]rAh]rBh]rCuubah Uhjhjubh)rD}rE(h}rF(h]rGh]rHh]rIh]rJh]rKUexprrLhuh hhNh hh]rMh)rN}rO(hjDh hh]rPh)rQ}rR(hjNh hh]rShX[source]rTrU}rV(hjQh Uubah Uh}rW(h]rXhah]rYh]rZh]r[h]r\uubah Uh}r](Urefdocr^hUrefidr_jh]r`h]raU refexplicitrbUreftyperchh]rdh]reU refdomainrfhh]rgU reftargetrhX_modules/transaction/interfacesriuubah UhNhjubeh XafterCompletion(transaction)rjhjhj ubj )rk}rl(h}rm(h]rnh]roh]rph]rqh]rruh hhKh jh]rsj)rt}ru(h}rv(h]rwh]rxh]ryh]rzh]r{uh hhKh jhj h XAHook that is called by the transaction after completing a commit.r|h]r}hXAHook that is called by the transaction after completing a commit.r~r}r(hjth j|ubahjkubah Uhjhj ubeh Uhj hjfubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX>newTransaction() (transaction.interfaces.ISynchronizer method)h&Utrah]rh]ruh hhNh jmh]rh UhX@docstring of transaction.interfaces.ISynchronizer.newTransactionrhjfubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXISynchronizer.newTransactionrhhh]rh&ah]rh]rhjh]rh&ah]rhuh hhKh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rhXnewTransactionrr}r(hjh Uubah XnewTransactionrhjhjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh hh]rj)r}r(hjh jh]rhX transactionrr}r(hjh Uubah X transactionrh}r(h]rh]rh]rh]rh]ruubah Uhjhjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh XnewTransaction(transaction)rhjhjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh X2Hook that is called at the start of a transaction.rh]rhX2Hook that is called at the start of a transaction.rr}r(hjh jubahjubj)r}r(h}r(h]rh]rh]rh]r h]r uh hhKh jhjh XdThis hook is called when, and only when, a transaction manager's begin() method is called explictly.r h]r hXdThis hook is called when, and only when, a transaction manager's begin() method is called explictly.r r}r(hjh j ubahjubeh Uhjhjubeh Uhjhjfubeh Uhjohjubeh UhNhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX2TransactionError (class in transaction.interfaces)h$Utrah]rh]ruh hhNh jmh]rh UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.TransactionErrorrhjubh)r}r(h}r(h]r h]r!h]r"hXclassr#jh]r$h]r%jj#jXpyr&uh hhNh jh]r'(h)r(}r)(h}r*(hXTransactionErrorr+hhXtransaction.interfacesr,r-}r.bh]r/h$ah]r0h]r1hUh]r2h$ah]r3huh hhNh hh]r4(j)r5}r6(h}r7(h]r8h]r9h]r:h]r;h]r<uh hhNh jh]r=hXclass r>r?}r@(hj5h Uubah Xclass rAhjohj(ubj)rB}rC(h}rD(h]rEh]rFh]rGh]rHh]rIuh hhNh jh]rJhXtransaction.interfaces.rKrL}rM(hjBh Uubah Xtransaction.interfaces.rNhjohj(ubh)rO}rP(h}rQ(h]rRh]rSh]rTh]rUh]rVuh hhNh hh]rWhXTransactionErrorrXrY}rZ(hjOh Uubah j+hjohj(ubh)r[}r\(h}r](h]r^h]r_h]r`h]rah]rbUexprrchuh hhNh hh]rdh)re}rf(hj[h hh]rgh)rh}ri(hjeh hh]rjhX[source]rkrl}rm(hjhh Uubah Uh}rn(h]rohah]rph]rqh]rrh]rsuubah Uh}rt(UrefdocruhUrefidrvj+h]rwh]rxU refexplicitryUreftyperzhh]r{h]r|U refdomainr}hh]r~U reftargetrX_modules/transaction/interfacesruubah UhNhj(ubeh j+hjohjubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh X7An error occurred due to normal transaction processing.rh]rhX7An error occurred due to normal transaction processing.rr}r(hjh jubahjubah Uhjohjubeh Uhjhjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX8TransactionFailedError (class in transaction.interfaces)hTUtrah]rh]ruh hhNh jmh]rh UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.TransactionFailedErrorrhjubh)r}r(h}r(h]rh]rh]rhXclassrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXTransactionFailedErrorrhhXtransaction.interfacesrr}rbh]rhTah]rh]rhUh]rhTah]rhuh hhNh hh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhXclass rr}r(hjh Uubah Xclass rhjohjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhXtransaction.interfaces.rr}r(hjh Uubah Xtransaction.interfaces.rhjohjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh hh]rhXTransactionFailedErrorrr}r(hjh Uubah jhjohjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjubeh jhjohjubj )r}r (h}r (h]r h]r h]r h]rh]ruh hhNh jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjh XDCannot perform an operation on a transaction that previously failed.rh]rhXDCannot perform an operation on a transaction that previously failed.rr}r(hjh jubahjubj)r}r(h}r (h]r!h]r"h]r#h]r$h]r%uh hhKh jhjh XAn attempt was made to commit a transaction, or to join a transaction, but this transaction previously raised an exception during an attempt to commit it. The transaction must be explicitly aborted, either by invoking abort() on the transaction, or begin() on its transaction manager.r&h]r'hXAn attempt was made to commit a transaction, or to join a transaction, but this transaction previously raised an exception during an attempt to commit it. The transaction must be explicitly aborted, either by invoking abort() on the transaction, or begin() on its transaction manager.r(r)}r*(hjh j&ubahjubeh Uhjohjubeh Uhjhjubj`)r+}r,(h}r-(h]r.h]r/h]r0Uentriesr1]r2(jiX3DoomedTransaction (class in transaction.interfaces)h3Utr3ah]r4h]r5uh hhNh jmh]r6h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.DoomedTransactionr7hjubh)r8}r9(h}r:(h]r;h]r<h]r=hXclassr>jh]r?h]r@jj>jXpyrAuh hhNh jh]rB(h)rC}rD(h}rE(hXDoomedTransactionrFhhXtransaction.interfacesrGrH}rIbh]rJh3ah]rKh]rLhUh]rMh3ah]rNhuh hhNh hh]rO(j)rP}rQ(h}rR(h]rSh]rTh]rUh]rVh]rWuh hhNh jh]rXhXclass rYrZ}r[(hjPh Uubah Xclass r\hjohjCubj)r]}r^(h}r_(h]r`h]rah]rbh]rch]rduh hhNh jh]rehXtransaction.interfaces.rfrg}rh(hj]h Uubah Xtransaction.interfaces.rihjohjCubh)rj}rk(h}rl(h]rmh]rnh]roh]rph]rquh hhNh hh]rrhXDoomedTransactionrsrt}ru(hjjh Uubah jFhjohjCubh)rv}rw(h}rx(h]ryh]rzh]r{h]r|h]r}Uexprr~huh hhNh hh]rh)r}r(hjvh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjFh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX_modules/transaction/interfacesruubah UhNhjCubeh jFhjohj8ubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhj7h X8A commit was attempted on a transaction that was doomed.rh]rhX8A commit was attempted on a transaction that was doomed.rr}r(hjh jubahjubah Uhjohj8ubeh Uhj7hjubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX0TransientError (class in transaction.interfaces)hUtrah]rh]ruh hhNh jmh]rh UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/interfaces.py:docstring of transaction.interfaces.TransientErrorrhjubh)r}r(h}r(h]rh]rh]rhXclassrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXTransientErrorrhhXtransaction.interfacesrr}rbh]rhah]rh]rhUh]rhah]rhuh hhNh hh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhXclass rr}r(hjh Uubah Xclass rhjohjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhXtransaction.interfaces.rr}r(hjh Uubah Xtransaction.interfaces.rhjohjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh hh]rhXTransientErrorrr}r(hjh Uubah jhjohjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]r h)r }r (hjh hh]r hX[source]r r}r(hj h Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]r U reftargetr!X_modules/transaction/interfacesr"uubah UhNhjubeh jhjohjubj )r#}r$(h}r%(h]r&h]r'h]r(h]r)h]r*uh hhNh jh]r+(j)r,}r-(h}r.(h]r/h]r0h]r1h]r2h]r3uh hhKh jhjh X3An error has occured when performing a transaction.r4h]r5hX3An error has occured when performing a transaction.r6r7}r8(hj,h j4ubahj#ubj)r9}r:(h}r;(h]r<h]r=h]r>h]r?h]r@uh hhKh jhjh X9It's possible that retrying the transaction will succeed.rAh]rBhX9It's possible that retrying the transaction will succeed.rCrD}rE(hj9h jAubahj#ubeh Uhjohjubeh Uhjhjubeh Uhhhj)rF}rG(h}rH(h]rIh]rJh]rKhjah]rLh]rMh_auh hhKh jh]rN(j)rO}rP(h}rQ(h]rRh]rSh]rTh]rUh]rVuh hhKh jh]rW(h)rX}rY(h}rZ(j>hj?j@NjANh]r[h]r\U refexplicitr]Ureftyper^Xmodr_h]r`h]raU refdomainrbXpyrch]rdjLX transactionreuhKh hhhh X:mod:`transaction`rfh]rgjP)rh}ri(hjXh jSh]rjhX transactionrkrl}rm(hjhh Uubah jfh}rn(h]ro(jZjcXpy-modrpeh]rqh]rrh]rsh]rtuubahjOubhX API Referencerurv}rw(hjOh X API Referencerxubeh X :mod:`transaction` API ReferenceryhhhjFubjj)rz}r{(h}r|(h]r}h]r~h]r(Xmodule-transaction._transactionrhheh]rh]rhauh hhK:h jh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhK:h jh]rhX API Objectsrr}r(hjh X API Objectsrubah jhhhjzubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX!transaction._transaction (module)Xmodule-transaction._transactionUtrah]rh]ruh hhK=h jmh]rh Uhhhjzubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX/Transaction (class in transaction._transaction)h Utrah]rh]ruh hhNh jmh]rh UhNhjzubh)r}r(h}r(h]rh]rh]rhXclassrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hX TransactionrhhXtransaction._transactionrr}rbh]rh ah]rh]rhUh]rh ah]rhuh hhNh hh]r(j)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhXclass rr}r(hjh Uubah Xclass rhjohjubj)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rhXtransaction._transaction.rr}r(hjh Uubah Xtransaction._transaction.rhjohjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh hh]rhX Transactionrr}r(hjh Uubah jhjohjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh hh]r(j)r}r(hjh jh]rhXsynchronizers=Nonerr}r(hjh Uubah Xsynchronizers=Nonerh}r(h]rh]rh]rh]rh]ruubj)r}r(hjh jh]rhX manager=Nonerr}r(hjh Uubah X manager=Nonerh}r(h]rh]rh]rh]rh]r uubeh Uhjohjubh)r }r (h}r (h]r h]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hj h hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]r h]r!h]r"uubah Uh}r#(Urefdocr$hUrefidr%jh]r&h]r'U refexplicitr(Ureftyper)hh]r*h]r+U refdomainr,hh]r-U reftargetr.X!_modules/transaction/_transactionr/uubah UhNhjubeh X-Transaction(synchronizers=None, manager=None)r0hjohjubj )r1}r2(h}r3(h]r4h]r5h]r6h]r7h]r8uh hhNh jh]r9(j`)r:}r;(h}r<(h]r=h]r>h]r?Uentriesr@]rA(jiX8isDoomed() (transaction._transaction.Transaction method)hDUtrBah]rCh]rDuh hhNh jmh]rEh UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.isDoomedrFhj1ubh)rG}rH(h}rI(h]rJh]rKh]rLhXmethodrMjh]rNh]rOjjMjXpyrPuh hhNh jh]rQ(h)rR}rS(h}rT(hXTransaction.isDoomedrUhhXtransaction._transactionrVrW}rXbh]rYhDah]rZh]r[hjh]r\hDah]r]huh hhNh hh]r^(h)r_}r`(h}ra(h]rbh]rch]rdh]reh]rfuh hhNh hh]rghXisDoomedrhri}rj(hj_h Uubah XisDoomedrkhjohjRubh)rl}rm(h}rn(h]roh]rph]rqh]rrh]rsuh hhNh hh]rth UhjohjRubh)ru}rv(h}rw(h]rxh]ryh]rzh]r{h]r|Uexprr}huh hhNh hh]r~h)r}r(hjuh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r(h]rhah]rh]rh]rh]ruubah Uh}r(UrefdocrhUrefidrjUh]rh]rU refexplicitrUreftyperhh]rh]rU refdomainrhh]rU reftargetrX!_modules/transaction/_transactionruubah UhNhjRubeh XTransaction.isDoomed()rhjohjGubj )r}r(h}r(h]rh]rh]rh]rh]ruh hhNh jh]rj)r}r(h}r(h]rh]rh]rh]rh]ruh hhKh jhjFh XSee ITransaction.rh]rhXSee ITransaction.rr}r(hjh jubahjubah UhjohjGubeh UhjFhj1ubj`)r}r(h}r(h]rh]rh]rUentriesr]r(jiX4doom() (transaction._transaction.Transaction method)h@Utrah]rh]ruh hhNh jmh]rh UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.doomrhj1ubh)r}r(h}r(h]rh]rh]rhXmethodrjh]rh]rjjjXpyruh hhNh jh]r(h)r}r(h}r(hXTransaction.doomrhhXtransaction._transactionrr}rbh]rh@ah]rh]rhjh]rh@ah]rhuh hhNh hh]r(h)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh hh]rhXdoomrr}r(hjh Uubah Xdoomrhjohjubh)r}r(h}r(h]rh]rh]rh]rh]ruh hhNh hh]rh Uhjohjubh)r}r(h}r(h]rh]rh]rh]rh]rUexprrhuh hhNh hh]rh)r}r(hjh hh]rh)r}r(hjh hh]rhX[source]rr}r(hjh Uubah Uh}r (h]r hah]r h]r h]r h]r uubah Uh}r (Urefdocr hUrefidr jh]r h]r U refexplicitr Ureftyper hh]r h]r U refdomainr hh]r U reftargetr X!_modules/transaction/_transactionr uubah UhNhjubeh XTransaction.doom()r hjohjubj )r }r (h}r (h]r h]r h]r h]r h]r uh hhNh jh]r j)r }r (h}r (h]r h]r! h]r" h]r# h]r$ uh hhKh jhjh XSee ITransaction.r% h]r& hXSee ITransaction.r' r( }r) (hj h j% ubahj ubah Uhjohjubeh Uhjhj1ubj`)r* }r+ (h}r, (h]r- h]r. h]r/ Uentriesr0 ]r1 (jiX4join() (transaction._transaction.Transaction method)hZUtr2 ah]r3 h]r4 uh hhNh jmh]r5 h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.joinr6 hj1ubh)r7 }r8 (h}r9 (h]r: h]r; h]r< hXmethodr= jh]r> h]r? jj= jXpyr@ uh hhNh jh]rA (h)rB }rC (h}rD (hXTransaction.joinrE hhXtransaction._transactionrF rG }rH bh]rI hZah]rJ h]rK hjh]rL hZah]rM huh hhNh hh]rN (h)rO }rP (h}rQ (h]rR h]rS h]rT h]rU h]rV uh hhNh hh]rW hXjoinrX rY }rZ (hjO h Uubah Xjoinr[ hjohjB ubh)r\ }r] (h}r^ (h]r_ h]r` h]ra h]rb h]rc uh hhNh hh]rd j)re }rf (hj\ h jh]rg hXresourcerh ri }rj (hje h Uubah Xresourcerk h}rl (h]rm h]rn h]ro h]rp h]rq uubah UhjohjB ubh)rr }rs (h}rt (h]ru h]rv h]rw h]rx h]ry Uexprrz huh hhNh hh]r{ h)r| }r} (hjr h hh]r~ h)r }r (hj| h hh]r hX[source]r r }r (hj h Uubah Uh}r (h]r hah]r h]r h]r h]r uubah Uh}r (Urefdocr hUrefidr jE h]r h]r U refexplicitr Ureftyper hh]r h]r U refdomainr hh]r U reftargetr X!_modules/transaction/_transactionr uubah UhNhjB ubeh XTransaction.join(resource)r hjohj7 ubj )r }r (h}r (h]r h]r h]r h]r h]r uh hhNh jh]r j)r }r (h}r (h]r h]r h]r h]r h]r uh hhKh jhj6 h XSee ITransaction.r h]r hXSee ITransaction.r r }r (hj h j ubahj ubah Uhjohj7 ubeh Uhj6 hj1ubj`)r }r (h}r (h]r h]r h]r Uentriesr ]r (jiX9savepoint() (transaction._transaction.Transaction method)h2Utr ah]r h]r uh hhNh jmh]r h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.savepointr hj1ubh)r }r (h}r (h]r h]r h]r hXmethodr jh]r h]r jj jXpyr uh hhNh jh]r (h)r }r (h}r (hXTransaction.savepointr hhXtransaction._transactionr r }r bh]r h2ah]r h]r hjh]r h2ah]r huh hhNh hh]r (h)r }r (h}r (h]r h]r h]r h]r h]r uh hhNh hh]r hX savepointr r }r (hj h Uubah X savepointr hjohj ubh)r }r (h}r (h]r h]r h]r h]r h]r uh hhNh hh]r j)r }r (hj h jh]r hXoptimistic=Falser r }r (hj h Uubah Xoptimistic=Falser h}r (h]r h]r h]r h]r h]r uubah Uhjohj ubh)r }r (h}r (h]r h]r h]r h]r h]r Uexprr huh hhNh hh]r!h)r!}r!(hj h hh]r!h)r!}r!(hj!h hh]r!hX[source]r!r!}r !(hj!h Uubah Uh}r !(h]r !hah]r !h]r !h]r!h]r!uubah Uh}r!(Urefdocr!hUrefidr!j h]r!h]r!U refexplicitr!Ureftyper!hh]r!h]r!U refdomainr!hh]r!U reftargetr!X!_modules/transaction/_transactionr!uubah UhNhj ubeh X'Transaction.savepoint(optimistic=False)r!hjohj ubj )r!}r!(h}r !(h]r!!h]r"!h]r#!h]r$!h]r%!uh hhNh jh]r&!j)r'!}r(!(h}r)!(h]r*!h]r+!h]r,!h]r-!h]r.!uh hhKh jhj h XSee ITransaction.r/!h]r0!hXSee ITransaction.r1!r2!}r3!(hj'!h j/!ubahj!ubah Uhjohj ubeh Uhj hj1ubj`)r4!}r5!(h}r6!(h]r7!h]r8!h]r9!Uentriesr:!]r;!(jiX8register() (transaction._transaction.Transaction method)h;Utr!uh hhNh jmh]r?!h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.registerr@!hj1ubh)rA!}rB!(h}rC!(h]rD!h]rE!h]rF!hXmethodrG!jh]rH!h]rI!jjG!jXpyrJ!uh hhNh jh]rK!(h)rL!}rM!(h}rN!(hXTransaction.registerrO!hhXtransaction._transactionrP!rQ!}rR!bh]rS!h;ah]rT!h]rU!hjh]rV!h;ah]rW!huh hhNh hh]rX!(h)rY!}rZ!(h}r[!(h]r\!h]r]!h]r^!h]r_!h]r`!uh hhNh hh]ra!hXregisterrb!rc!}rd!(hjY!h Uubah Xregisterre!hjohjL!ubh)rf!}rg!(h}rh!(h]ri!h]rj!h]rk!h]rl!h]rm!uh hhNh hh]rn!j)ro!}rp!(hjf!h jh]rq!hXobjrr!rs!}rt!(hjo!h Uubah Xobjru!h}rv!(h]rw!h]rx!h]ry!h]rz!h]r{!uubah UhjohjL!ubh)r|!}r}!(h}r~!(h]r!h]r!h]r!h]r!h]r!Uexprr!huh hhNh hh]r!h)r!}r!(hj|!h hh]r!h)r!}r!(hj!h hh]r!hX[source]r!r!}r!(hj!h Uubah Uh}r!(h]r!hah]r!h]r!h]r!h]r!uubah Uh}r!(Urefdocr!hUrefidr!jO!h]r!h]r!U refexplicitr!Ureftyper!hh]r!h]r!U refdomainr!hh]r!U reftargetr!X!_modules/transaction/_transactionr!uubah UhNhjL!ubeh XTransaction.register(obj)r!hjohjA!ubj )r!}r!(h}r!(h]r!h]r!h]r!h]r!h]r!uh hhNh jh]r!j)r!}r!(h}r!(h]r!h]r!h]r!h]r!h]r!uh hhKh jhj@!h XSee ITransaction.r!h]r!hXSee ITransaction.r!r!}r!(hj!h j!ubahj!ubah UhjohjA!ubeh Uhj@!hj1ubj`)r!}r!(h}r!(h]r!h]r!h]r!Uentriesr!]r!(jiX6commit() (transaction._transaction.Transaction method)h`Utr!ah]r!h]r!uh hhNh jmh]r!h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.commitr!hj1ubh)r!}r!(h}r!(h]r!h]r!h]r!hXmethodr!jh]r!h]r!jj!jXpyr!uh hhNh jh]r!(h)r!}r!(h}r!(hXTransaction.commitr!hhXtransaction._transactionr!r!}r!bh]r!h`ah]r!h]r!hjh]r!h`ah]r!huh hhNh hh]r!(h)r!}r!(h}r!(h]r!h]r!h]r!h]r!h]r!uh hhNh hh]r!hXcommitr!r!}r!(hj!h Uubah Xcommitr!hjohj!ubh)r!}r!(h}r!(h]r!h]r!h]r!h]r!h]r!uh hhNh hh]r!h Uhjohj!ubh)r!}r!(h}r!(h]r!h]r!h]r!h]r!h]r!Uexprr!huh hhNh hh]r!h)r!}r!(hj!h hh]r"h)r"}r"(hj!h hh]r"hX[source]r"r"}r"(hj"h Uubah Uh}r"(h]r"hah]r "h]r "h]r "h]r "uubah Uh}r "(Urefdocr"hUrefidr"j!h]r"h]r"U refexplicitr"Ureftyper"hh]r"h]r"U refdomainr"hh]r"U reftargetr"X!_modules/transaction/_transactionr"uubah UhNhj!ubeh XTransaction.commit()r"hjohj!ubj )r"}r"(h}r"(h]r"h]r"h]r "h]r!"h]r""uh hhNh jh]r#"j)r$"}r%"(h}r&"(h]r'"h]r("h]r)"h]r*"h]r+"uh hhKh jhj!h XSee ITransaction.r,"h]r-"hXSee ITransaction.r."r/"}r0"(hj$"h j,"ubahj"ubah Uhjohj!ubeh Uhj!hj1ubj`)r1"}r2"(h}r3"(h]r4"h]r5"h]r6"Uentriesr7"]r8"(jiXDgetBeforeCommitHooks() (transaction._transaction.Transaction method)h[Utr9"ah]r:"h]r;"uh hhNh jmh]r<"h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.getBeforeCommitHooksr="hj1ubh)r>"}r?"(h}r@"(h]rA"h]rB"h]rC"hXmethodrD"jh]rE"h]rF"jjD"jXpyrG"uh hhNh jh]rH"(h)rI"}rJ"(h}rK"(hX Transaction.getBeforeCommitHooksrL"hhXtransaction._transactionrM"rN"}rO"bh]rP"h[ah]rQ"h]rR"hjh]rS"h[ah]rT"huh hhNh hh]rU"(h)rV"}rW"(h}rX"(h]rY"h]rZ"h]r["h]r\"h]r]"uh hhNh hh]r^"hXgetBeforeCommitHooksr_"r`"}ra"(hjV"h Uubah XgetBeforeCommitHooksrb"hjohjI"ubh)rc"}rd"(h}re"(h]rf"h]rg"h]rh"h]ri"h]rj"uh hhNh hh]rk"h UhjohjI"ubh)rl"}rm"(h}rn"(h]ro"h]rp"h]rq"h]rr"h]rs"Uexprrt"huh hhNh hh]ru"h)rv"}rw"(hjl"h hh]rx"h)ry"}rz"(hjv"h hh]r{"hX[source]r|"r}"}r~"(hjy"h Uubah Uh}r"(h]r"hah]r"h]r"h]r"h]r"uubah Uh}r"(Urefdocr"hUrefidr"jL"h]r"h]r"U refexplicitr"Ureftyper"hh]r"h]r"U refdomainr"hh]r"U reftargetr"X!_modules/transaction/_transactionr"uubah UhNhjI"ubeh X"Transaction.getBeforeCommitHooks()r"hjohj>"ubj )r"}r"(h}r"(h]r"h]r"h]r"h]r"h]r"uh hhNh jh]r"j)r"}r"(h}r"(h]r"h]r"h]r"h]r"h]r"uh hhKh jhj="h XSee ITransaction.r"h]r"hXSee ITransaction.r"r"}r"(hj"h j"ubahj"ubah Uhjohj>"ubeh Uhj="hj1ubj`)r"}r"(h}r"(h]r"h]r"h]r"Uentriesr"]r"(jiXCaddBeforeCommitHook() (transaction._transaction.Transaction method)h*Utr"ah]r"h]r"uh hhNh jmh]r"h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.addBeforeCommitHookr"hj1ubh)r"}r"(h}r"(h]r"h]r"h]r"hXmethodr"jh]r"h]r"jj"jXpyr"uh hhNh jh]r"(h)r"}r"(h}r"(hXTransaction.addBeforeCommitHookr"hhXtransaction._transactionr"r"}r"bh]r"h*ah]r"h]r"hjh]r"h*ah]r"huh hhNh hh]r"(h)r"}r"(h}r"(h]r"h]r"h]r"h]r"h]r"uh hhNh hh]r"hXaddBeforeCommitHookr"r"}r"(hj"h Uubah XaddBeforeCommitHookr"hjohj"ubh)r"}r"(h}r"(h]r"h]r"h]r"h]r"h]r"uh hhNh hh]r"(j)r"}r"(hj"h jh]r"hXhookr"r"}r"(hj"h Uubah Xhookr"h}r"(h]r"h]r"h]r"h]r"h]r"uubj)r"}r"(hj"h jh]r"hXargs=()r"r"}r"(hj"h Uubah Xargs=()r"h}r"(h]r"h]r"h]r"h]r"h]r"uubj)r"}r"(hj"h jh]r#hXkws=Noner#r#}r#(hj"h Uubah Xkws=Noner#h}r#(h]r#h]r#h]r#h]r #h]r #uubeh Uhjohj"ubh)r #}r #(h}r #(h]r#h]r#h]r#h]r#h]r#Uexprr#huh hhNh hh]r#h)r#}r#(hj #h hh]r#h)r#}r#(hj#h hh]r#hX[source]r#r#}r#(hj#h Uubah Uh}r#(h]r#hah]r #h]r!#h]r"#h]r##uubah Uh}r$#(Urefdocr%#hUrefidr&#j"h]r'#h]r(#U refexplicitr)#Ureftyper*#hh]r+#h]r,#U refdomainr-#hh]r.#U reftargetr/#X!_modules/transaction/_transactionr0#uubah UhNhj"ubeh X8Transaction.addBeforeCommitHook(hook, args=(), kws=None)r1#hjohj"ubj )r2#}r3#(h}r4#(h]r5#h]r6#h]r7#h]r8#h]r9#uh hhNh jh]r:#j)r;#}r<#(h}r=#(h]r>#h]r?#h]r@#h]rA#h]rB#uh hhKh jhj"h XSee ITransaction.rC#h]rD#hXSee ITransaction.rE#rF#}rG#(hj;#h jC#ubahj2#ubah Uhjohj"ubeh Uhj"hj1ubj`)rH#}rI#(h}rJ#(h]rK#h]rL#h]rM#UentriesrN#]rO#(jiXCgetAfterCommitHooks() (transaction._transaction.Transaction method)h6UtrP#ah]rQ#h]rR#uh hhNh jmh]rS#h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.getAfterCommitHooksrT#hj1ubh)rU#}rV#(h}rW#(h]rX#h]rY#h]rZ#hXmethodr[#jh]r\#h]r]#jj[#jXpyr^#uh hhNh jh]r_#(h)r`#}ra#(h}rb#(hXTransaction.getAfterCommitHooksrc#hhXtransaction._transactionrd#re#}rf#bh]rg#h6ah]rh#h]ri#hjh]rj#h6ah]rk#huh hhNh hh]rl#(h)rm#}rn#(h}ro#(h]rp#h]rq#h]rr#h]rs#h]rt#uh hhNh hh]ru#hXgetAfterCommitHooksrv#rw#}rx#(hjm#h Uubah XgetAfterCommitHooksry#hjohj`#ubh)rz#}r{#(h}r|#(h]r}#h]r~#h]r#h]r#h]r#uh hhNh hh]r#h Uhjohj`#ubh)r#}r#(h}r#(h]r#h]r#h]r#h]r#h]r#Uexprr#huh hhNh hh]r#h)r#}r#(hj#h hh]r#h)r#}r#(hj#h hh]r#hX[source]r#r#}r#(hj#h Uubah Uh}r#(h]r#hah]r#h]r#h]r#h]r#uubah Uh}r#(Urefdocr#hUrefidr#jc#h]r#h]r#U refexplicitr#Ureftyper#hh]r#h]r#U refdomainr#hh]r#U reftargetr#X!_modules/transaction/_transactionr#uubah UhNhj`#ubeh X!Transaction.getAfterCommitHooks()r#hjohjU#ubj )r#}r#(h}r#(h]r#h]r#h]r#h]r#h]r#uh hhNh jh]r#j)r#}r#(h}r#(h]r#h]r#h]r#h]r#h]r#uh hhKh jhjT#h XSee ITransaction.r#h]r#hXSee ITransaction.r#r#}r#(hj#h j#ubahj#ubah UhjohjU#ubeh UhjT#hj1ubj`)r#}r#(h}r#(h]r#h]r#h]r#Uentriesr#]r#(jiXBaddAfterCommitHook() (transaction._transaction.Transaction method)hIUtr#ah]r#h]r#uh hhNh jmh]r#h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.addAfterCommitHookr#hj1ubh)r#}r#(h}r#(h]r#h]r#h]r#hXmethodr#jh]r#h]r#jj#jXpyr#uh hhNh jh]r#(h)r#}r#(h}r#(hXTransaction.addAfterCommitHookr#hhXtransaction._transactionr#r#}r#bh]r#hIah]r#h]r#hjh]r#hIah]r#huh hhNh hh]r#(h)r#}r#(h}r#(h]r#h]r#h]r#h]r#h]r#uh hhNh hh]r#hXaddAfterCommitHookr#r#}r#(hj#h Uubah XaddAfterCommitHookr#hjohj#ubh)r#}r#(h}r#(h]r#h]r#h]r#h]r#h]r#uh hhNh hh]r#(j)r#}r#(hj#h jh]r#hXhookr#r#}r$(hj#h Uubah Xhookr$h}r$(h]r$h]r$h]r$h]r$h]r$uubj)r$}r $(hj#h jh]r $hXargs=()r $r $}r $(hj$h Uubah Xargs=()r$h}r$(h]r$h]r$h]r$h]r$h]r$uubj)r$}r$(hj#h jh]r$hXkws=Noner$r$}r$(hj$h Uubah Xkws=Noner$h}r$(h]r$h]r$h]r$h]r $h]r!$uubeh Uhjohj#ubh)r"$}r#$(h}r$$(h]r%$h]r&$h]r'$h]r($h]r)$Uexprr*$huh hhNh hh]r+$h)r,$}r-$(hj"$h hh]r.$h)r/$}r0$(hj,$h hh]r1$hX[source]r2$r3$}r4$(hj/$h Uubah Uh}r5$(h]r6$hah]r7$h]r8$h]r9$h]r:$uubah Uh}r;$(Urefdocr<$hUrefidr=$j#h]r>$h]r?$U refexplicitr@$UreftyperA$hh]rB$h]rC$U refdomainrD$hh]rE$U reftargetrF$X!_modules/transaction/_transactionrG$uubah UhNhj#ubeh X7Transaction.addAfterCommitHook(hook, args=(), kws=None)rH$hjohj#ubj )rI$}rJ$(h}rK$(h]rL$h]rM$h]rN$h]rO$h]rP$uh hhNh jh]rQ$j)rR$}rS$(h}rT$(h]rU$h]rV$h]rW$h]rX$h]rY$uh hhKh jhj#h XSee ITransaction.rZ$h]r[$hXSee ITransaction.r\$r]$}r^$(hjR$h jZ$ubahjI$ubah Uhjohj#ubeh Uhj#hj1ubj`)r_$}r`$(h}ra$(h]rb$h]rc$h]rd$Uentriesre$]rf$(jiX5abort() (transaction._transaction.Transaction method)hSUtrg$ah]rh$h]ri$uh hhNh jmh]rj$h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.abortrk$hj1ubh)rl$}rm$(h}rn$(h]ro$h]rp$h]rq$hXmethodrr$jh]rs$h]rt$jjr$jXpyru$uh hhNh jh]rv$(h)rw$}rx$(h}ry$(hXTransaction.abortrz$hhXtransaction._transactionr{$r|$}r}$bh]r~$hSah]r$h]r$hjh]r$hSah]r$huh hhNh hh]r$(h)r$}r$(h}r$(h]r$h]r$h]r$h]r$h]r$uh hhNh hh]r$hXabortr$r$}r$(hj$h Uubah Xabortr$hjohjw$ubh)r$}r$(h}r$(h]r$h]r$h]r$h]r$h]r$uh hhNh hh]r$h Uhjohjw$ubh)r$}r$(h}r$(h]r$h]r$h]r$h]r$h]r$Uexprr$huh hhNh hh]r$h)r$}r$(hj$h hh]r$h)r$}r$(hj$h hh]r$hX[source]r$r$}r$(hj$h Uubah Uh}r$(h]r$hah]r$h]r$h]r$h]r$uubah Uh}r$(Urefdocr$hUrefidr$jz$h]r$h]r$U refexplicitr$Ureftyper$hh]r$h]r$U refdomainr$hh]r$U reftargetr$X!_modules/transaction/_transactionr$uubah UhNhjw$ubeh XTransaction.abort()r$hjohjl$ubj )r$}r$(h}r$(h]r$h]r$h]r$h]r$h]r$uh hhNh jh]r$j)r$}r$(h}r$(h]r$h]r$h]r$h]r$h]r$uh hhKh jhjk$h XSee ITransaction.r$h]r$hXSee ITransaction.r$r$}r$(hj$h j$ubahj$ubah Uhjohjl$ubeh Uhjk$hj1ubj`)r$}r$(h}r$(h]r$h]r$h]r$Uentriesr$]r$(jiX4note() (transaction._transaction.Transaction method)h%hh]r?%h]r@%U refdomainrA%hh]rB%U reftargetrC%X!_modules/transaction/_transactionrD%uubah UhNhj$ubeh XTransaction.note(text)rE%hjohj$ubj )rF%}rG%(h}rH%(h]rI%h]rJ%h]rK%h]rL%h]rM%uh hhNh jh]rN%j)rO%}rP%(h}rQ%(h]rR%h]rS%h]rT%h]rU%h]rV%uh hhKh jhj$h XSee ITransaction.rW%h]rX%hXSee ITransaction.rY%rZ%}r[%(hjO%h jW%ubahjF%ubah Uhjohj$ubeh Uhj$hj1ubj`)r\%}r]%(h}r^%(h]r_%h]r`%h]ra%Uentriesrb%]rc%(jiX7setUser() (transaction._transaction.Transaction method)h^Utrd%ah]re%h]rf%uh hhNh jmh]rg%h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.setUserrh%hj1ubh)ri%}rj%(h}rk%(h]rl%h]rm%h]rn%hXmethodro%jh]rp%h]rq%jjo%jXpyrr%uh hhNh jh]rs%(h)rt%}ru%(h}rv%(hXTransaction.setUserrw%hhXtransaction._transactionrx%ry%}rz%bh]r{%h^ah]r|%h]r}%hjh]r~%h^ah]r%huh hhNh hh]r%(h)r%}r%(h}r%(h]r%h]r%h]r%h]r%h]r%uh hhNh hh]r%hXsetUserr%r%}r%(hj%h Uubah XsetUserr%hjohjt%ubh)r%}r%(h}r%(h]r%h]r%h]r%h]r%h]r%uh hhNh hh]r%(j)r%}r%(hj%h jh]r%hX user_namer%r%}r%(hj%h Uubah X user_namer%h}r%(h]r%h]r%h]r%h]r%h]r%uubj)r%}r%(hj%h jh]r%hXpath='/'r%r%}r%(hj%h Uubah Xpath='/'r%h}r%(h]r%h]r%h]r%h]r%h]r%uubeh Uhjohjt%ubh)r%}r%(h}r%(h]r%h]r%h]r%h]r%h]r%Uexprr%huh hhNh hh]r%h)r%}r%(hj%h hh]r%h)r%}r%(hj%h hh]r%hX[source]r%r%}r%(hj%h Uubah Uh}r%(h]r%hah]r%h]r%h]r%h]r%uubah Uh}r%(Urefdocr%hUrefidr%jw%h]r%h]r%U refexplicitr%Ureftyper%hh]r%h]r%U refdomainr%hh]r%U reftargetr%X!_modules/transaction/_transactionr%uubah UhNhjt%ubeh X(Transaction.setUser(user_name, path='/')r%hjohji%ubj )r%}r%(h}r%(h]r%h]r%h]r%h]r%h]r%uh hhNh jh]r%j)r%}r%(h}r%(h]r%h]r%h]r%h]r%h]r%uh hhKh jhjh%h XSee ITransaction.r%h]r%hXSee ITransaction.r%r%}r%(hj%h j%ubahj%ubah Uhjohji%ubeh Uhjh%hj1ubj`)r%}r%(h}r%(h]r%h]r%h]r%Uentriesr%]r%(jiX?setExtendedInfo() (transaction._transaction.Transaction method)h.Utr%ah]r%h]r%uh hhNh jmh]r%h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Transaction.setExtendedInfor%hj1ubh)r%}r%(h}r%(h]r%h]r%h]r&hXmethodr&jh]r&h]r&jj&jXpyr&uh hhNh jh]r&(h)r&}r&(h}r&(hXTransaction.setExtendedInfor &hhXtransaction._transactionr &r &}r &bh]r &h.ah]r&h]r&hjh]r&h.ah]r&huh hhNh hh]r&(h)r&}r&(h}r&(h]r&h]r&h]r&h]r&h]r&uh hhNh hh]r&hXsetExtendedInfor&r&}r&(hj&h Uubah XsetExtendedInfor&hjohj&ubh)r &}r!&(h}r"&(h]r#&h]r$&h]r%&h]r&&h]r'&uh hhNh hh]r(&(j)r)&}r*&(hj &h jh]r+&hXnamer,&r-&}r.&(hj)&h Uubah Xnamer/&h}r0&(h]r1&h]r2&h]r3&h]r4&h]r5&uubj)r6&}r7&(hj &h jh]r8&hXvaluer9&r:&}r;&(hj6&h Uubah Xvaluer<&h}r=&(h]r>&h]r?&h]r@&h]rA&h]rB&uubeh Uhjohj&ubh)rC&}rD&(h}rE&(h]rF&h]rG&h]rH&h]rI&h]rJ&UexprrK&huh hhNh hh]rL&h)rM&}rN&(hjC&h hh]rO&h)rP&}rQ&(hjM&h hh]rR&hX[source]rS&rT&}rU&(hjP&h Uubah Uh}rV&(h]rW&hah]rX&h]rY&h]rZ&h]r[&uubah Uh}r\&(Urefdocr]&hUrefidr^&j &h]r_&h]r`&U refexplicitra&Ureftyperb&hh]rc&h]rd&U refdomainre&hh]rf&U reftargetrg&X!_modules/transaction/_transactionrh&uubah UhNhj&ubeh X(Transaction.setExtendedInfo(name, value)ri&hjohj%ubj )rj&}rk&(h}rl&(h]rm&h]rn&h]ro&h]rp&h]rq&uh hhNh jh]rr&j)rs&}rt&(h}ru&(h]rv&h]rw&h]rx&h]ry&h]rz&uh hhKh jhj%h XSee ITransaction.r{&h]r|&hXSee ITransaction.r}&r~&}r&(hjs&h j{&ubahjj&ubah Uhjohj%ubeh Uhj%hj1ubeh Uhjohjubeh UhNhjzubj`)r&}r&(h}r&(h]r&h]r&h]r&Uentriesr&]r&(jiX-Savepoint (class in transaction._transaction)hUtr&ah]r&h]r&uh hhNh jmh]r&h UhNhjzubh)r&}r&(h}r&(h]r&h]r&h]r&hXclassr&jh]r&h]r&jj&jXpyr&uh hhNh jh]r&(h)r&}r&(h}r&(hX Savepointr&hhXtransaction._transactionr&r&}r&bh]r&hah]r&h]r&hUh]r&hah]r&huh hhNh hh]r&(j)r&}r&(h}r&(h]r&h]r&h]r&h]r&h]r&uh hhNh jh]r&hXclass r&r&}r&(hj&h Uubah Xclass r&hjohj&ubj)r&}r&(h}r&(h]r&h]r&h]r&h]r&h]r&uh hhNh jh]r&hXtransaction._transaction.r&r&}r&(hj&h Uubah Xtransaction._transaction.r&hjohj&ubh)r&}r&(h}r&(h]r&h]r&h]r&h]r&h]r&uh hhNh hh]r&hX Savepointr&r&}r&(hj&h Uubah j&hjohj&ubh)r&}r&(h}r&(h]r&h]r&h]r&h]r&h]r&uh hhNh hh]r&(j)r&}r&(hj&h jh]r&hX transactionr&r&}r&(hj&h Uubah X transactionr&h}r&(h]r&h]r&h]r&h]r&h]r&uubj)r&}r&(hj&h jh]r&hX optimisticr&r&}r&(hj&h Uubah X optimisticr&h}r&(h]r&h]r&h]r&h]r&h]r&uubj)r&}r&(hj&h jh]r&hX *resourcesr&r&}r&(hj&h Uubah X *resourcesr&h}r&(h]r&h]r&h]r&h]r&h]r&uubeh Uhjohj&ubh)r&}r&(h}r&(h]r&h]r&h]r&h]r'h]r'Uexprr'huh hhNh hh]r'h)r'}r'(hj&h hh]r'h)r'}r'(hj'h hh]r 'hX[source]r 'r '}r '(hj'h Uubah Uh}r '(h]r'hah]r'h]r'h]r'h]r'uubah Uh}r'(Urefdocr'hUrefidr'j&h]r'h]r'U refexplicitr'Ureftyper'hh]r'h]r'U refdomainr'hh]r'U reftargetr'X!_modules/transaction/_transactionr'uubah UhNhj&ubeh X.Savepoint(transaction, optimistic, *resources)r 'hjohj&ubj )r!'}r"'(h}r#'(h]r$'h]r%'h]r&'h]r''h]r('uh hhNh jh]r)'(j)r*'}r+'(h}r,'(h]r-'h]r.'h]r/'h]r0'h]r1'uh hhKh jhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Savepointr2'h XTransaction savepoint.r3'h]r4'hXTransaction savepoint.r5'r6'}r7'(hj*'h j3'ubahj!'ubj)r8'}r9'(h}r:'(h]r;'h]r<'h]r='h]r>'h]r?'uh hhKh jhj2'h X^Transaction savepoints coordinate savepoints for data managers participating in a transaction.r@'h]rA'hX^Transaction savepoints coordinate savepoints for data managers participating in a transaction.rB'rC'}rD'(hj8'h j@'ubahj!'ubj`)rE'}rF'(h}rG'(h]rH'h]rI'h]rJ'UentriesrK']rL'(jiX6rollback() (transaction._transaction.Savepoint method)hUtrM'ah]rN'h]rO'uh hhNh jmh]rP'h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_transaction.py:docstring of transaction._transaction.Savepoint.rollbackrQ'hj!'ubh)rR'}rS'(h}rT'(h]rU'h]rV'h]rW'hXmethodrX'jh]rY'h]rZ'jjX'jXpyr['uh hhNh jh]r\'(h)r]'}r^'(h}r_'(hXSavepoint.rollbackr`'hhXtransaction._transactionra'rb'}rc'bh]rd'hah]re'h]rf'hj&h]rg'hah]rh'huh hhNh hh]ri'(h)rj'}rk'(h}rl'(h]rm'h]rn'h]ro'h]rp'h]rq'uh hhNh hh]rr'hXrollbackrs'rt'}ru'(hjj'h Uubah Xrollbackrv'hjohj]'ubh)rw'}rx'(h}ry'(h]rz'h]r{'h]r|'h]r}'h]r~'uh hhNh hh]r'h Uhjohj]'ubh)r'}r'(h}r'(h]r'h]r'h]r'h]r'h]r'Uexprr'huh hhNh hh]r'h)r'}r'(hj'h hh]r'h)r'}r'(hj'h hh]r'hX[source]r'r'}r'(hj'h Uubah Uh}r'(h]r'hah]r'h]r'h]r'h]r'uubah Uh}r'(Urefdocr'hUrefidr'j`'h]r'h]r'U refexplicitr'Ureftyper'hh]r'h]r'U refdomainr'hh]r'U reftargetr'X!_modules/transaction/_transactionr'uubah UhNhj]'ubeh XSavepoint.rollback()r'hjohjR'ubj )r'}r'(h}r'(h]r'h]r'h]r'h]r'h]r'uh hhNh jh]r'j)r'}r'(h}r'(h]r'h]r'h]r'h]r'h]r'uh hhKh jhjQ'h XSee ISavepoint.r'h]r'hXSee ISavepoint.r'r'}r'(hj'h j'ubahj'ubah UhjohjR'ubeh UhjQ'hj!'ubeh Uhjohj&ubeh UhNhjzubcdocutils.nodes target r')r'}r'(h}r'(Uismodr'h]r'Xmodule-transaction._managerr'ah]r'h]r'h]r'h]r'uh hhKGh Utargetr'h]r'h Uhhhjzubj`)r'}r'(h}r'(h]r'h]r'h]r'Uentriesr']r'(jiXtransaction._manager (module)Xmodule-transaction._managerUtr'ah]r'h]r'uh hhKGh jmh]r'h Uhhhjzubj`)r'}r'(h}r'(h]r'h]r'h]r'Uentriesr']r'(jiX2TransactionManager (class in transaction._manager)hGUtr'ah]r'h]r'uh hhNh jmh]r'h UhNhjzubh)r'}r'(h}r'(h]r'h]r'h]r'hXclassr'jh]r'h]r'jj'jXpyr'uh hhNh jh]r'(h)r'}r'(h}r'(hXTransactionManagerr'hhXtransaction._managerr'r'}r'bh]r'hGah]r'h]r'hUh]r'hGah]r'huh hhNh hh]r'(j)r'}r'(h}r'(h]r'h]r'h]r'h]r(h]r(uh hhNh jh]r(hXclass r(r(}r((hj'h Uubah Xclass r(hjohj'ubj)r(}r((h}r ((h]r (h]r (h]r (h]r (h]r(uh hhNh jh]r(hXtransaction._manager.r(r(}r((hj(h Uubah Xtransaction._manager.r(hjohj'ubh)r(}r((h}r((h]r(h]r(h]r(h]r(h]r(uh hhNh hh]r(hXTransactionManagerr(r(}r((hj(h Uubah j'hjohj'ubh)r (}r!((h}r"((h]r#(h]r$(h]r%(h]r&(h]r'(Uexprr((huh hhNh hh]r)(h)r*(}r+((hj (h hh]r,(h)r-(}r.((hj*(h hh]r/(hX[source]r0(r1(}r2((hj-(h Uubah Uh}r3((h]r4(hah]r5(h]r6(h]r7(h]r8(uubah Uh}r9((Urefdocr:(hUrefidr;(j'h]r<(h]r=(U refexplicitr>(Ureftyper?(hh]r@(h]rA(U refdomainrB(hh]rC(U reftargetrD(X_modules/transaction/_managerrE(uubah UhNhj'ubeh XTransactionManager()rF(hjohj'ubj )rG(}rH((h}rI((h]rJ(h]rK(h]rL(h]rM(h]rN(uh hhNh jh]rO((j`)rP(}rQ((h}rR((h]rS(h]rT(h]rU(UentriesrV(]rW((jiX<__enter__() (transaction._manager.TransactionManager method)hUUtrX(ah]rY(h]rZ(uh hhNh jmh]r[(h UhhhjG(ubh)r\(}r]((h}r^((h]r_(h]r`(h]ra(hXmethodrb(jh]rc(h]rd(jjb(jXpyre(uh hhNh jh]rf((h)rg(}rh((h}ri((hXTransactionManager.__enter__rj(hhXtransaction._managerrk(rl(}rm(bh]rn(hUah]ro(h]rp(hj'h]rq(hUah]rr(huh hhNh hh]rs((h)rt(}ru((h}rv((h]rw(h]rx(h]ry(h]rz(h]r{(uh hhNh hh]r|(hX __enter__r}(r~(}r((hjt(h Uubah X __enter__r(hjohjg(ubh)r(}r((h}r((h]r(h]r(h]r(h]r(h]r(uh hhNh hh]r(h Uhjohjg(ubeh XTransactionManager.__enter__()r(hjohj\(ubj )r(}r((h}r((h]r(h]r(h]r(h]r(h]r(uh hhNh jh]r(j)r(}r((h}r((h]r(h]r(h]r(h]r(h]r(uh hhKNh jhhh XAlias for :meth:`get`r(h]r((hX Alias for r(r(}r((hj(h X Alias for r(ubh)r(}r((h}r((j>hj?j@Xtransaction._managerr(jAj'h]r(h]r(U refexplicitr(Ureftyper(Xmethr(h]r(h]r(U refdomainr(Xpyr(h]r(jLXgetr(uhKh hhhh X :meth:`get`r(h]r(jP)r(}r((hj(h jSh]r(hXget()r(r(}r((hj(h Uubah j(h}r((h]r((jZj(Xpy-methr(eh]r(h]r(h]r(h]r(uubahj(ubehj(ubah Uhjohj\(ubeh UhhhjG(ubj`)r(}r((h}r((h]r(h]r(h]r(Uentriesr(]r((jiX;__exit__() (transaction._manager.TransactionManager method)hUtr(ah]r(h]r(uh hhNh jmh]r(h UhhhjG(ubh)r(}r((h}r((h]r(h]r(h]r(hXmethodr(jh]r(h]r(jj(jXpyr(uh hhNh jh]r((h)r(}r((h}r((hXTransactionManager.__exit__r(hhXtransaction._managerr(r(}r(bh]r(hah]r(h]r(hj'h]r(hah]r(huh hhNh hh]r((h)r(}r((h}r((h]r(h]r(h]r(h]r(h]r(uh hhNh hh]r(hX__exit__r(r(}r((hj(h Uubah X__exit__r(hjohj(ubh)r(}r((h}r((h]r(h]r(h]r(h]r(h]r(uh hhNh hh]r((j)r(}r((hj(h jh]r(hXtr(}r((hj(h Uubah Xth}r((h]r)h]r)h]r)h]r)h]r)uubj)r)}r)(hj(h jh]r)hXvr)}r )(hj)h Uubah Xvh}r )(h]r )h]r )h]r )h]r)h]r)uubj)r)}r)(hj(h jh]r)hXtbr)r)}r)(hj)h Uubah Xtbr)h}r)(h]r)h]r)h]r)h]r)h]r)uubeh Uhjohj(ubh)r)}r)(h}r)(h]r )h]r!)h]r")h]r#)h]r$)Uexprr%)huh hhNh hh]r&)h)r')}r()(hj)h hh]r))h)r*)}r+)(hj')h hh]r,)hX[source]r-)r.)}r/)(hj*)h Uubah Uh}r0)(h]r1)hah]r2)h]r3)h]r4)h]r5)uubah Uh}r6)(Urefdocr7)hUrefidr8)j(h]r9)h]r:)U refexplicitr;)Ureftyper<)hh]r=)h]r>)U refdomainr?)hh]r@)U reftargetrA)X_modules/transaction/_managerrB)uubah UhNhj(ubeh X%TransactionManager.__exit__(t, v, tb)rC)hjohj(ubj )rD)}rE)(h}rF)(h]rG)h]rH)h]rI)h]rJ)h]rK)uh hhNh jh]rL)j)rM)}rN)(h}rO)(h]rP)h]rQ)h]rR)h]rS)h]rT)uh hhKRh jhhh X>On error, aborts the current transaction. Otherwise, commits.rU)h]rV)hX>On error, aborts the current transaction. Otherwise, commits.rW)rX)}rY)(hjM)h jU)ubahjD)ubah Uhjohj(ubeh UhhhjG(ubj`)rZ)}r[)(h}r\)(h]r])h]r^)h]r_)Uentriesr`)]ra)(jiX8begin() (transaction._manager.TransactionManager method)hJUtrb)ah]rc)h]rd)uh hhNh jmh]re)h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_manager.py:docstring of transaction._manager.TransactionManager.beginrf)hjG(ubh)rg)}rh)(h}ri)(h]rj)h]rk)h]rl)hXmethodrm)jh]rn)h]ro)jjm)jXpyrp)uh hhNh jh]rq)(h)rr)}rs)(h}rt)(hXTransactionManager.beginru)hhXtransaction._managerrv)rw)}rx)bh]ry)hJah]rz)h]r{)hj'h]r|)hJah]r})huh hhNh hh]r~)(h)r)}r)(h}r)(h]r)h]r)h]r)h]r)h]r)uh hhNh hh]r)hXbeginr)r)}r)(hj)h Uubah Xbeginr)hjohjr)ubh)r)}r)(h}r)(h]r)h]r)h]r)h]r)h]r)uh hhNh hh]r)h Uhjohjr)ubh)r)}r)(h}r)(h]r)h]r)h]r)h]r)h]r)Uexprr)huh hhNh hh]r)h)r)}r)(hj)h hh]r)h)r)}r)(hj)h hh]r)hX[source]r)r)}r)(hj)h Uubah Uh}r)(h]r)hah]r)h]r)h]r)h]r)uubah Uh}r)(Urefdocr)hUrefidr)ju)h]r)h]r)U refexplicitr)Ureftyper)hh]r)h]r)U refdomainr)hh]r)U reftargetr)X_modules/transaction/_managerr)uubah UhNhjr)ubeh XTransactionManager.begin()r)hjohjg)ubj )r)}r)(h}r)(h]r)h]r)h]r)h]r)h]r)uh hhNh jh]r)j)r)}r)(h}r)(h]r)h]r)h]r)h]r)h]r)uh hhKh jhjf)h XSee ITransactionManager.r)h]r)hXSee ITransactionManager.r)r)}r)(hj)h j)ubahj)ubah Uhjohjg)ubeh Uhjf)hjG(ubj`)r)}r)(h}r)(h]r)h]r)h]r)Uentriesr)]r)(jiX6get() (transaction._manager.TransactionManager method)hUtr)ah]r)h]r)uh hhNh jmh]r)h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_manager.py:docstring of transaction._manager.TransactionManager.getr)hjG(ubh)r)}r)(h}r)(h]r)h]r)h]r)hXmethodr)jh]r)h]r)jj)jXpyr)uh hhNh jh]r)(h)r)}r)(h}r)(hXTransactionManager.getr)hhXtransaction._managerr)r)}r)bh]r)hah]r)h]r)hj'h]r)hah]r)huh hhNh hh]r)(h)r)}r)(h}r)(h]r)h]r)h]r)h]r)h]r)uh hhNh hh]r)hXgetr*r*}r*(hj)h Uubah Xgetr*hjohj)ubh)r*}r*(h}r*(h]r*h]r*h]r *h]r *h]r *uh hhNh hh]r *h Uhjohj)ubh)r *}r*(h}r*(h]r*h]r*h]r*h]r*h]r*Uexprr*huh hhNh hh]r*h)r*}r*(hj *h hh]r*h)r*}r*(hj*h hh]r*hX[source]r*r*}r*(hj*h Uubah Uh}r *(h]r!*hah]r"*h]r#*h]r$*h]r%*uubah Uh}r&*(Urefdocr'*hUrefidr(*j)h]r)*h]r**U refexplicitr+*Ureftyper,*hh]r-*h]r.*U refdomainr/*hh]r0*U reftargetr1*X_modules/transaction/_managerr2*uubah UhNhj)ubeh XTransactionManager.get()r3*hjohj)ubj )r4*}r5*(h}r6*(h]r7*h]r8*h]r9*h]r:*h]r;*uh hhNh jh]r<*j)r=*}r>*(h}r?*(h]r@*h]rA*h]rB*h]rC*h]rD*uh hhKh jhj)h XSee ITransactionManager.rE*h]rF*hXSee ITransactionManager.rG*rH*}rI*(hj=*h jE*ubahj4*ubah Uhjohj)ubeh Uhj)hjG(ubj`)rJ*}rK*(h}rL*(h]rM*h]rN*h]rO*UentriesrP*]rQ*(jiX@registerSynch() (transaction._manager.TransactionManager method)h%UtrR*ah]rS*h]rT*uh hhNh jmh]rU*h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_manager.py:docstring of transaction._manager.TransactionManager.registerSynchrV*hjG(ubh)rW*}rX*(h}rY*(h]rZ*h]r[*h]r\*hXmethodr]*jh]r^*h]r_*jj]*jXpyr`*uh hhNh jh]ra*(h)rb*}rc*(h}rd*(hX TransactionManager.registerSynchre*hhXtransaction._managerrf*rg*}rh*bh]ri*h%ah]rj*h]rk*hj'h]rl*h%ah]rm*huh hhNh hh]rn*(h)ro*}rp*(h}rq*(h]rr*h]rs*h]rt*h]ru*h]rv*uh hhNh hh]rw*hX registerSynchrx*ry*}rz*(hjo*h Uubah X registerSynchr{*hjohjb*ubh)r|*}r}*(h}r~*(h]r*h]r*h]r*h]r*h]r*uh hhNh hh]r*j)r*}r*(hj|*h jh]r*hXsynchr*r*}r*(hj*h Uubah Xsynchr*h}r*(h]r*h]r*h]r*h]r*h]r*uubah Uhjohjb*ubh)r*}r*(h}r*(h]r*h]r*h]r*h]r*h]r*Uexprr*huh hhNh hh]r*h)r*}r*(hj*h hh]r*h)r*}r*(hj*h hh]r*hX[source]r*r*}r*(hj*h Uubah Uh}r*(h]r*hah]r*h]r*h]r*h]r*uubah Uh}r*(Urefdocr*hUrefidr*je*h]r*h]r*U refexplicitr*Ureftyper*hh]r*h]r*U refdomainr*hh]r*U reftargetr*X_modules/transaction/_managerr*uubah UhNhjb*ubeh X'TransactionManager.registerSynch(synch)r*hjohjW*ubj )r*}r*(h}r*(h]r*h]r*h]r*h]r*h]r*uh hhNh jh]r*j)r*}r*(h}r*(h]r*h]r*h]r*h]r*h]r*uh hhKh jhjV*h XSee ITransactionManager.r*h]r*hXSee ITransactionManager.r*r*}r*(hj*h j*ubahj*ubah UhjohjW*ubeh UhjV*hjG(ubj`)r*}r*(h}r*(h]r*h]r*h]r*Uentriesr*]r*(jiXBunregisterSynch() (transaction._manager.TransactionManager method)hRUtr*ah]r*h]r*uh hhNh jmh]r*h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_manager.py:docstring of transaction._manager.TransactionManager.unregisterSynchr*hjG(ubh)r*}r*(h}r*(h]r*h]r*h]r*hXmethodr*jh]r*h]r*jj*jXpyr*uh hhNh jh]r*(h)r*}r*(h}r*(hX"TransactionManager.unregisterSynchr*hhXtransaction._managerr*r*}r*bh]r*hRah]r*h]r*hj'h]r*hRah]r*huh hhNh hh]r*(h)r*}r*(h}r*(h]r*h]r*h]r*h]r*h]r*uh hhNh hh]r*hXunregisterSynchr*r*}r*(hj*h Uubah XunregisterSynchr+hjohj*ubh)r+}r+(h}r+(h]r+h]r+h]r+h]r+h]r+uh hhNh hh]r +j)r +}r +(hj+h jh]r +hXsynchr +r+}r+(hj +h Uubah Xsynchr+h}r+(h]r+h]r+h]r+h]r+h]r+uubah Uhjohj*ubh)r+}r+(h}r+(h]r+h]r+h]r+h]r+h]r+Uexprr+huh hhNh hh]r +h)r!+}r"+(hj+h hh]r#+h)r$+}r%+(hj!+h hh]r&+hX[source]r'+r(+}r)+(hj$+h Uubah Uh}r*+(h]r++hah]r,+h]r-+h]r.+h]r/+uubah Uh}r0+(Urefdocr1+hUrefidr2+j*h]r3+h]r4+U refexplicitr5+Ureftyper6+hh]r7+h]r8+U refdomainr9+hh]r:+U reftargetr;+X_modules/transaction/_managerr<+uubah UhNhj*ubeh X)TransactionManager.unregisterSynch(synch)r=+hjohj*ubj )r>+}r?+(h}r@+(h]rA+h]rB+h]rC+h]rD+h]rE+uh hhNh jh]rF+j)rG+}rH+(h}rI+(h]rJ+h]rK+h]rL+h]rM+h]rN+uh hhKh jhj*h XSee ITransactionManager.rO+h]rP+hXSee ITransactionManager.rQ+rR+}rS+(hjG+h jO+ubahj>+ubah Uhjohj*ubeh Uhj*hjG(ubj`)rT+}rU+(h}rV+(h]rW+h]rX+h]rY+UentriesrZ+]r[+(jiX;isDoomed() (transaction._manager.TransactionManager method)hPUtr\+ah]r]+h]r^+uh hhNh jmh]r_+h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_manager.py:docstring of transaction._manager.TransactionManager.isDoomedr`+hjG(ubh)ra+}rb+(h}rc+(h]rd+h]re+h]rf+hXmethodrg+jh]rh+h]ri+jjg+jXpyrj+uh hhNh jh]rk+(h)rl+}rm+(h}rn+(hXTransactionManager.isDoomedro+hhXtransaction._managerrp+rq+}rr+bh]rs+hPah]rt+h]ru+hj'h]rv+hPah]rw+huh hhNh hh]rx+(h)ry+}rz+(h}r{+(h]r|+h]r}+h]r~+h]r+h]r+uh hhNh hh]r+hXisDoomedr+r+}r+(hjy+h Uubah XisDoomedr+hjohjl+ubh)r+}r+(h}r+(h]r+h]r+h]r+h]r+h]r+uh hhNh hh]r+h Uhjohjl+ubh)r+}r+(h}r+(h]r+h]r+h]r+h]r+h]r+Uexprr+huh hhNh hh]r+h)r+}r+(hj+h hh]r+h)r+}r+(hj+h hh]r+hX[source]r+r+}r+(hj+h Uubah Uh}r+(h]r+hah]r+h]r+h]r+h]r+uubah Uh}r+(Urefdocr+hUrefidr+jo+h]r+h]r+U refexplicitr+Ureftyper+hh]r+h]r+U refdomainr+hh]r+U reftargetr+X_modules/transaction/_managerr+uubah UhNhjl+ubeh XTransactionManager.isDoomed()r+hjohja+ubj )r+}r+(h}r+(h]r+h]r+h]r+h]r+h]r+uh hhNh jh]r+j)r+}r+(h}r+(h]r+h]r+h]r+h]r+h]r+uh hhKh jhj`+h XSee ITransactionManager.r+h]r+hXSee ITransactionManager.r+r+}r+(hj+h j+ubahj+ubah Uhjohja+ubeh Uhj`+hjG(ubj`)r+}r+(h}r+(h]r+h]r+h]r+Uentriesr+]r+(jiX7doom() (transaction._manager.TransactionManager method)h4Utr+ah]r+h]r+uh hhNh jmh]r+h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_manager.py:docstring of transaction._manager.TransactionManager.doomr+hjG(ubh)r+}r+(h}r+(h]r+h]r+h]r+hXmethodr+jh]r+h]r+jj+jXpyr+uh hhNh jh]r+(h)r+}r+(h}r+(hXTransactionManager.doomr+hhXtransaction._managerr+r+}r+bh]r+h4ah]r+h]r+hj'h]r+h4ah]r+huh hhNh hh]r+(h)r+}r+(h}r+(h]r+h]r+h]r+h]r+h]r+uh hhNh hh]r+hXdoomr+r+}r+(hj+h Uubah Xdoomr+hjohj+ubh)r+}r+(h}r,(h]r,h]r,h]r,h]r,h]r,uh hhNh hh]r,h Uhjohj+ubh)r,}r,(h}r ,(h]r ,h]r ,h]r ,h]r ,h]r,Uexprr,huh hhNh hh]r,h)r,}r,(hj,h hh]r,h)r,}r,(hj,h hh]r,hX[source]r,r,}r,(hj,h Uubah Uh}r,(h]r,hah]r,h]r,h]r,h]r,uubah Uh}r ,(Urefdocr!,hUrefidr",j+h]r#,h]r$,U refexplicitr%,Ureftyper&,hh]r',h]r(,U refdomainr),hh]r*,U reftargetr+,X_modules/transaction/_managerr,,uubah UhNhj+ubeh XTransactionManager.doom()r-,hjohj+ubj )r.,}r/,(h}r0,(h]r1,h]r2,h]r3,h]r4,h]r5,uh hhNh jh]r6,j)r7,}r8,(h}r9,(h]r:,h]r;,h]r<,h]r=,h]r>,uh hhKh jhj+h XSee ITransactionManager.r?,h]r@,hXSee ITransactionManager.rA,rB,}rC,(hj7,h j?,ubahj.,ubah Uhjohj+ubeh Uhj+hjG(ubj`)rD,}rE,(h}rF,(h]rG,h]rH,h]rI,UentriesrJ,]rK,(jiX9commit() (transaction._manager.TransactionManager method)hQUtrL,ah]rM,h]rN,uh hhNh jmh]rO,h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_manager.py:docstring of transaction._manager.TransactionManager.commitrP,hjG(ubh)rQ,}rR,(h}rS,(h]rT,h]rU,h]rV,hXmethodrW,jh]rX,h]rY,jjW,jXpyrZ,uh hhNh jh]r[,(h)r\,}r],(h}r^,(hXTransactionManager.commitr_,hhXtransaction._managerr`,ra,}rb,bh]rc,hQah]rd,h]re,hj'h]rf,hQah]rg,huh hhNh hh]rh,(h)ri,}rj,(h}rk,(h]rl,h]rm,h]rn,h]ro,h]rp,uh hhNh hh]rq,hXcommitrr,rs,}rt,(hji,h Uubah Xcommitru,hjohj\,ubh)rv,}rw,(h}rx,(h]ry,h]rz,h]r{,h]r|,h]r},uh hhNh hh]r~,h Uhjohj\,ubh)r,}r,(h}r,(h]r,h]r,h]r,h]r,h]r,Uexprr,huh hhNh hh]r,h)r,}r,(hj,h hh]r,h)r,}r,(hj,h hh]r,hX[source]r,r,}r,(hj,h Uubah Uh}r,(h]r,hah]r,h]r,h]r,h]r,uubah Uh}r,(Urefdocr,hUrefidr,j_,h]r,h]r,U refexplicitr,Ureftyper,hh]r,h]r,U refdomainr,hh]r,U reftargetr,X_modules/transaction/_managerr,uubah UhNhj\,ubeh XTransactionManager.commit()r,hjohjQ,ubj )r,}r,(h}r,(h]r,h]r,h]r,h]r,h]r,uh hhNh jh]r,j)r,}r,(h}r,(h]r,h]r,h]r,h]r,h]r,uh hhKh jhjP,h XSee ITransactionManager.r,h]r,hXSee ITransactionManager.r,r,}r,(hj,h j,ubahj,ubah UhjohjQ,ubeh UhjP,hjG(ubj`)r,}r,(h}r,(h]r,h]r,h]r,Uentriesr,]r,(jiX8abort() (transaction._manager.TransactionManager method)hUtr,ah]r,h]r,uh hhNh jmh]r,h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_manager.py:docstring of transaction._manager.TransactionManager.abortr,hjG(ubh)r,}r,(h}r,(h]r,h]r,h]r,hXmethodr,jh]r,h]r,jj,jXpyr,uh hhNh jh]r,(h)r,}r,(h}r,(hXTransactionManager.abortr,hhXtransaction._managerr,r,}r,bh]r,hah]r,h]r,hj'h]r,hah]r,huh hhNh hh]r,(h)r,}r,(h}r,(h]r,h]r,h]r,h]r,h]r,uh hhNh hh]r,hXabortr,r,}r,(hj,h Uubah Xabortr,hjohj,ubh)r,}r,(h}r,(h]r,h]r,h]r,h]r,h]r,uh hhNh hh]r,h Uhjohj,ubh)r,}r,(h}r,(h]r,h]r,h]r,h]r,h]r,Uexprr,huh hhNh hh]r-h)r-}r-(hj,h hh]r-h)r-}r-(hj-h hh]r-hX[source]r-r-}r -(hj-h Uubah Uh}r -(h]r -hah]r -h]r -h]r-h]r-uubah Uh}r-(Urefdocr-hUrefidr-j,h]r-h]r-U refexplicitr-Ureftyper-hh]r-h]r-U refdomainr-hh]r-U reftargetr-X_modules/transaction/_managerr-uubah UhNhj,ubeh XTransactionManager.abort()r-hjohj,ubj )r-}r-(h}r -(h]r!-h]r"-h]r#-h]r$-h]r%-uh hhNh jh]r&-j)r'-}r(-(h}r)-(h]r*-h]r+-h]r,-h]r--h]r.-uh hhKh jhj,h XSee ITransactionManager.r/-h]r0-hXSee ITransactionManager.r1-r2-}r3-(hj'-h j/-ubahj-ubah Uhjohj,ubeh Uhj,hjG(ubj`)r4-}r5-(h}r6-(h]r7-h]r8-h]r9-Uentriesr:-]r;-(jiX<savepoint() (transaction._manager.TransactionManager method)hUtr<-ah]r=-h]r>-uh hhNh jmh]r?-h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_manager.py:docstring of transaction._manager.TransactionManager.savepointr@-hjG(ubh)rA-}rB-(h}rC-(h]rD-h]rE-h]rF-hXmethodrG-jh]rH-h]rI-jjG-jXpyrJ-uh hhNh jh]rK-(h)rL-}rM-(h}rN-(hXTransactionManager.savepointrO-hhXtransaction._managerrP-rQ-}rR-bh]rS-hah]rT-h]rU-hj'h]rV-hah]rW-huh hhNh hh]rX-(h)rY-}rZ-(h}r[-(h]r\-h]r]-h]r^-h]r_-h]r`-uh hhNh hh]ra-hX savepointrb-rc-}rd-(hjY-h Uubah X savepointre-hjohjL-ubh)rf-}rg-(h}rh-(h]ri-h]rj-h]rk-h]rl-h]rm-uh hhNh hh]rn-j)ro-}rp-(hjf-h jh]rq-hXoptimistic=Falserr-rs-}rt-(hjo-h Uubah Xoptimistic=Falseru-h}rv-(h]rw-h]rx-h]ry-h]rz-h]r{-uubah UhjohjL-ubh)r|-}r}-(h}r~-(h]r-h]r-h]r-h]r-h]r-Uexprr-huh hhNh hh]r-h)r-}r-(hj|-h hh]r-h)r-}r-(hj-h hh]r-hX[source]r-r-}r-(hj-h Uubah Uh}r-(h]r-hah]r-h]r-h]r-h]r-uubah Uh}r-(Urefdocr-hUrefidr-jO-h]r-h]r-U refexplicitr-Ureftyper-hh]r-h]r-U refdomainr-hh]r-U reftargetr-X_modules/transaction/_managerr-uubah UhNhjL-ubeh X.TransactionManager.savepoint(optimistic=False)r-hjohjA-ubj )r-}r-(h}r-(h]r-h]r-h]r-h]r-h]r-uh hhNh jh]r-j)r-}r-(h}r-(h]r-h]r-h]r-h]r-h]r-uh hhKh jhj@-h XSee ITransactionManager.r-h]r-hXSee ITransactionManager.r-r-}r-(hj-h j-ubahj-ubah UhjohjA-ubeh Uhj@-hjG(ubeh Uhjohj'ubeh UhNhjzubj`)r-}r-(h}r-(h]r-h]r-h]r-Uentriesr-]r-(jiX8ThreadTransactionManager (class in transaction._manager)h#Utr-ah]r-h]r-uh hhNh jmh]r-h UhX/home/tseaver/projects/Zope/ZODB/transaction/.tox/docs/lib/python2.6/site-packages/transaction/_manager.py:docstring of transaction._manager.ThreadTransactionManagerr-hjzubh)r-}r-(h}r-(h]r-h]r-h]r-hXclassr-jh]r-h]r-jj-jXpyr-uh hhNh jh]r-(h)r-}r-(h}r-(hXThreadTransactionManagerr-hhXtransaction._managerr-r-}r-bh]r-h#ah]r-h]r-hUh]r-h#ah]r-huh hhNh hh]r-(j)r-}r-(h}r-(h]r-h]r-h]r-h]r-h]r-uh hhNh jh]r-hXclass r-r-}r-(hj-h Uubah Xclass r-hjohj-ubj)r-}r-(h}r-(h]r-h]r-h]r-h]r-h]r-uh hhNh jh]r-hXtransaction._manager.r-r-}r-(hj-h Uubah Xtransaction._manager.r-hjohj-ubh)r-}r-(h}r-(h]r-h]r-h]r-h]r-h]r-uh hhNh hh]r.hXThreadTransactionManagerr.r.}r.(hj-h Uubah j-hjohj-ubh)r.}r.(h}r.(h]r.h]r.h]r .h]r .h]r .Uexprr .huh hhNh hh]r .h)r.}r.(hj.h hh]r.h)r.}r.(hj.h hh]r.hX[source]r.r.}r.(hj.h Uubah Uh}r.(h]r.hah]r.h]r.h]r.h]r.uubah Uh}r.(Urefdocr.hUrefidr.j-h]r .h]r!.U refexplicitr".Ureftyper#.hh]r$.h]r%.U refdomainr&.hh]r'.U reftargetr(.X_modules/transaction/_managerr).uubah UhNhj-ubeh XThreadTransactionManager()r*.hjohj-ubj )r+.}r,.(h}r-.(h]r..h]r/.h]r0.h]r1.h]r2.uh hhNh jh]r3.(j)r4.}r5.(h}r6.(h]r7.h]r8.h]r9.h]r:.h]r;.uh hhKh jhj-h X!Thread-aware transaction manager.r<.h]r=.hX!Thread-aware transaction manager.r>.r?.}r@.(hj4.h j<.ubahj+.ubj)rA.}rB.(h}rC.(h]rD.h]rE.h]rF.h]rG.h]rH.uh hhKh jhj-h X4Each thread is associated with a unique transaction.rI.h]rJ.hX4Each thread is associated with a unique transaction.rK.rL.}rM.(hjA.h jI.ubahj+.ubeh Uhjohj-ubeh Uhj-hjzubeh UhhhjFubeh UhhhhububububububhjL-hj]'hj$hBjh/jh!j h%jb*h(jh+j h8j>h`j!h;jL!hDjRhNjhQj\,hSjw$h3jChCjh*j"hajhbjhj&hjhjh'j!hh9j h=jxh>j$ h@jhEj_ hHj,hKj@hLj2hXj h jh4j+hj{hjwhjhj,hjRhjhjj'j'hYj:h,jh-jh.j&h0jhjjFh2j hRj*h:jhijhGj'jj')rX.}rY.(h}rZ.(Uismodr[.h]r\.jah]r].h]r^.h]r_.h]r`.uh hhKh j'h]ra.h UhhhjubhTjh$j(h[jI"hZjB hIj#h\j h5jh^jt%hPjl+uU decorationrb.Nh]rc.jFaU footnote_refsrd.}re.U current_linerf.NUsettingsrg.(cdocutils.frontend Values rh.ori.}rj.(U sectnum_xformrk.KU halt_levelrl.KU datestamprm.NU tab_widthrn.KU toc_backlinksro.Uentryrp.Uembed_stylesheetrq.Ugettext_compactrr.U report_levelrs.KUstrip_commentsrt.NU source_linkru.NUenvrv.NU smart_quotesrw.U_disable_configrx.NUsectsubtitle_xformry.Uinput_encoding_error_handlerrz.Ustrictr{.U rfc_base_urlr|.Uhttp://tools.ietf.org/html/r}.Ufile_insertion_enabledr~.Udebugr.NUwarning_streamr.NU strip_classesr.NUoutput_encodingr.Uutf-8r.U docinfo_xformr.KU source_urlr.NUdoctitle_xformr.Udump_internalsr.NUfootnote_backlinksr.KU id_prefixr.UUpep_referencesr.NjNUauto_id_prefixr.Uidr.Ustrip_elements_with_classesr.NUoutput_encoding_error_handlerr.j{.U _config_filesr.]r.U pep_base_urlr.Uhttp://www.python.org/dev/peps/r.U _destinationr.NUerror_encodingr.UUTF-8r.U tracebackr.Urfc_referencesr.NUexpose_internalsr.NUtrim_footnote_reference_spacer.Urecord_dependenciesr.NU_sourcer.U9/home/tseaver/projects/Zope/ZODB/transaction/docs/api.rstr.U raw_enabledr.KUconfigr.NU generatorr.NUerror_encoding_error_handlerr.Ubackslashreplacer.U language_coder.Uenr.Uinput_encodingr.U utf-8-sigr.Ustrict_visitorr.NUsyntax_highlightr.Ulongr.Uexit_status_levelr.KU dump_settingsr.NUcloak_email_addressesr.Udump_transformsr.NUdump_pseudo_xmlr.NUpep_file_url_templater.Upep-%04dr.ubUtransform_messagesr.]r.cdocutils.nodes system_message r.)r.}r.(h Usystem_messager.h]r.j)r.}r.(hj.h jh]r.hXAHyperlink target "module-transaction._manager" is not referenced.r.r.}r.(hj.h Uubah Uh}r.(h]r.h]r.h]r.h]r.h]r.uubah Uh}r.(Utyper.UINFOr.Ulevelr.KUliner.KGh]r.h]r.Usourcer.hh]r.h]r.h]r.uubaUautofootnote_startr.Kub.transaction-1.4.3/docs/_build/doctrees/environment.pickle0000664000175000017500000031241712312641212023507 0ustar tseavertseaver(csphinx.environment BuildEnvironment qoq}q(Uall_docsq}q(X convenienceqGAТ.0XdoomqGAТ(X savepointqGAТXresourcemanagerq GAТXhooksq GAТEX datamanagerq GAТĽXapiq GAТXindexq GAТFuU found_docsqc__builtin__ set q]q(hhhh h h h h eRqUimagesqcsphinx.util FilenameUniqDict q)qh]RqbU temp_dataq}qU doctreedirqXA/home/tseaver/projects/Zope/ZODB/transaction/docs/_build/doctreesqUdlfilesqh)qh]RqbU_viewcode_modulesq}q(cdocutils.nodes reprunicode qXtransaction.interfacesq q!}q"bXK############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## import zope.interface class ITransactionManager(zope.interface.Interface): """An object that manages a sequence of transactions. Applications use transaction managers to establish transaction boundaries. """ def begin(): """Begin a new transaction. If an existing transaction is in progress, it will be aborted. The newTransaction() method of registered synchronizers is called, passing the new transaction object. """ def get(): """Get the current transaction. """ def commit(): """Commit the current transaction. """ def abort(): """Abort the current transaction. """ def doom(): """Doom the current transaction. """ def isDoomed(): """Returns True if the current transaction is doomed, otherwise False. """ def savepoint(optimistic=False): """Create a savepoint from the current transaction. If the optimistic argument is true, then data managers that don't support savepoints can be used, but an error will be raised if the savepoint is rolled back. An ISavepoint object is returned. """ def registerSynch(synch): """Register an ISynchronizer. Synchronizers are notified about some major events in a transaction's life. See ISynchronizer for details. """ def unregisterSynch(synch): """Unregister an ISynchronizer. Synchronizers are notified about some major events in a transaction's life. See ISynchronizer for details. """ class ITransaction(zope.interface.Interface): """Object representing a running transaction. Objects with this interface may represent different transactions during their lifetime (.begin() can be called to start a new transaction using the same instance, although that example is deprecated and will go away in ZODB 3.6). """ user = zope.interface.Attribute( """A user name associated with the transaction. The format of the user name is defined by the application. The value is of Python type str. Storages record the user value, as meta-data, when a transaction commits. A storage may impose a limit on the size of the value; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or truncate the value). """) description = zope.interface.Attribute( """A textual description of the transaction. The value is of Python type str. Method note() is the intended way to set the value. Storages record the description, as meta-data, when a transaction commits. A storage may impose a limit on the size of the description; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or truncate the value). """) def commit(): """Finalize the transaction. This executes the two-phase commit algorithm for all IDataManager objects associated with the transaction. """ def abort(): """Abort the transaction. This is called from the application. This can only be called before the two-phase commit protocol has been started. """ def doom(): """Doom the transaction. Dooms the current transaction. This will cause DoomedTransactionException to be raised on any attempt to commit the transaction. Otherwise the transaction will behave as if it was active. """ def savepoint(optimistic=False): """Create a savepoint. If the optimistic argument is true, then data managers that don't support savepoints can be used, but an error will be raised if the savepoint is rolled back. An ISavepoint object is returned. """ def join(datamanager): """Add a data manager to the transaction. `datamanager` must provide the transactions.interfaces.IDataManager interface. """ def note(text): """Add text to the transaction description. This modifies the `.description` attribute; see its docs for more detail. First surrounding whitespace is stripped from `text`. If `.description` is currently an empty string, then the stripped text becomes its value, else two newlines and the stripped text are appended to `.description`. """ def setUser(user_name, path="/"): """Set the user name. path should be provided if needed to further qualify the identified user. This is a convenience method used by Zope. It sets the .user attribute to str(path) + " " + str(user_name). This sets the `.user` attribute; see its docs for more detail. """ def setExtendedInfo(name, value): """Add extension data to the transaction. name is the name of the extension property to set, of Python type str; value must be picklable. Multiple calls may be made to set multiple extension properties, provided the names are distinct. Storages record the extension data, as meta-data, when a transaction commits. A storage may impose a limit on the size of extension data; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or remove `` pairs). """ # deprecated38 def beforeCommitHook(__hook, *args, **kws): """Register a hook to call before the transaction is committed. THIS IS DEPRECATED IN ZODB 3.6. Use addBeforeCommitHook() instead. The specified hook function will be called after the transaction's commit method has been called, but before the commit process has been started. The hook will be passed the specified positional and keyword arguments. Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks. Hooks are called only for a top-level commit. A savepoint does not call any hooks. If the transaction is aborted, hooks are not called, and are discarded. Calling a hook "consumes" its registration too: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then beforeCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead. """ def addBeforeCommitHook(hook, args=(), kws=None): """Register a hook to call before the transaction is committed. The specified hook function will be called after the transaction's commit method has been called, but before the commit process has been started. The hook will be passed the specified positional (`args`) and keyword (`kws`) arguments. `args` is a sequence of positional arguments to be passed, defaulting to an empty tuple (no positional arguments are passed). `kws` is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed). Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks. Hooks are called only for a top-level commit. A savepoint creation does not call any hooks. If the transaction is aborted, hooks are not called, and are discarded. Calling a hook "consumes" its registration too: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then addBeforeCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead. """ def getBeforeCommitHooks(): """Return iterable producing the registered addBeforeCommit hooks. A triple (hook, args, kws) is produced for each registered hook. The hooks are produced in the order in which they would be invoked by a top-level transaction commit. """ def addAfterCommitHook(hook, args=(), kws=None): """Register a hook to call after a transaction commit attempt. The specified hook function will be called after the transaction commit succeeds or aborts. The first argument passed to the hook is a Boolean value, true if the commit succeeded, or false if the commit aborted. `args` specifies additional positional, and `kws` keyword, arguments to pass to the hook. `args` is a sequence of positional arguments to be passed, defaulting to an empty tuple (only the true/false success argument is passed). `kws` is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed). Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks. Hooks are called only for a top-level commit. A savepoint creation does not call any hooks. Calling a hook "consumes" its registration: hook registrations do not persist across transactions. If it's desired to call the same hook on every transaction commit, then addAfterCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager's registerSynch() method instead. """ def getAfterCommitHooks(): """Return iterable producing the registered addAfterCommit hooks. A triple (hook, args, kws) is produced for each registered hook. The hooks are produced in the order in which they would be invoked by a top-level transaction commit. """ class ITransactionDeprecated(zope.interface.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(zope.interface.Interface): """Objects that manage transactional storage. These objects may manage data for other objects, or they may manage non-object storages, such as relational databases. For example, a ZODB.Connection. Note that when some data is modified, that data's data manager should join a transaction so that data can be committed when the user commits the transaction. """ transaction_manager = zope.interface.Attribute( """The transaction manager (TM) used by this data manager. This is a public attribute, intended for read-only use. The value is an instance of ITransactionManager, typically set by the data manager's constructor. """) def abort(transaction): """Abort a transaction and forget all changes. Abort must be called outside of a two-phase commit. Abort is called by the transaction manager to abort transactions that are not yet in a two-phase commit. It may also be called when rolling back a savepoint made before the data manager joined the transaction. In any case, after abort is called, the data manager is no longer participating in the transaction. If there are new changes, the data manager must rejoin the transaction. """ # Two-phase commit protocol. These methods are called by the ITransaction # object associated with the transaction being committed. The sequence # of calls normally follows this regular expression: # tpc_begin commit tpc_vote (tpc_finish | tpc_abort) def tpc_begin(transaction): """Begin commit of a transaction, starting the two-phase commit. transaction is the ITransaction instance associated with the transaction being committed. """ def commit(transaction): """Commit modifications to registered objects. Save changes to be made persistent if the transaction commits (if tpc_finish is called later). If tpc_abort is called later, changes must not persist. This includes conflict detection and handling. If no conflicts or errors occur, the data manager should be prepared to make the changes persist when tpc_finish is called. """ def tpc_vote(transaction): """Verify that a data manager can commit the transaction. This is the last chance for a data manager to vote 'no'. A data manager votes 'no' by raising an exception. transaction is the ITransaction instance associated with the transaction being committed. """ def tpc_finish(transaction): """Indicate confirmation that the transaction is done. Make all changes to objects modified by this transaction persist. transaction is the ITransaction instance associated with the transaction being committed. This should never fail. If this raises an exception, the database is not expected to maintain consistency; it's a serious error. """ def tpc_abort(transaction): """Abort a transaction. This is called by a transaction manager to end a two-phase commit on the data manager. Abandon all changes to objects modified by this transaction. transaction is the ITransaction instance associated with the transaction being committed. This should never fail. """ def sortKey(): """Return a key to use for ordering registered DataManagers. In order to guarantee a total ordering, keys must be strings. ZODB uses a global sort order to prevent deadlock when it commits transactions involving multiple resource managers. The resource manager must define a sortKey() method that provides a global ordering for resource managers. """ # Alternate version: #"""Return a consistent sort key for this connection. # #This allows ordering multiple connections that use the same storage in #a consistent manner. This is unique for the lifetime of a connection, #which is good enough to avoid ZEO deadlocks. #""" class ISavepointDataManager(IDataManager): def savepoint(): """Return a data-manager savepoint (IDataManagerSavepoint). """ class IDataManagerSavepoint(zope.interface.Interface): """Savepoint for data-manager changes for use in transaction savepoints. Datamanager savepoints are used by, and only by, transaction savepoints. Note that data manager savepoints don't have any notion of, or responsibility for, validity. It isn't the responsibility of data-manager savepoints to prevent multiple rollbacks or rollbacks after transaction termination. Preventing invalid savepoint rollback is the responsibility of transaction rollbacks. Application code should never use data-manager savepoints. """ def rollback(): """Rollback any work done since the savepoint. """ class ISavepoint(zope.interface.Interface): """A transaction savepoint. """ def rollback(): """Rollback any work done since the savepoint. InvalidSavepointRollbackError is raised if the savepoint isn't valid. """ valid = zope.interface.Attribute( "Boolean indicating whether the savepoint is valid") class InvalidSavepointRollbackError(Exception): """Attempt to rollback an invalid savepoint. A savepoint may be invalid because: - The surrounding transaction has committed or aborted. - An earlier savepoint in the same transaction has been rolled back. """ class ISynchronizer(zope.interface.Interface): """Objects that participate in the transaction-boundary notification API. """ def beforeCompletion(transaction): """Hook that is called by the transaction at the start of a commit. """ def afterCompletion(transaction): """Hook that is called by the transaction after completing a commit. """ def newTransaction(transaction): """Hook that is called at the start of a transaction. This hook is called when, and only when, a transaction manager's begin() method is called explictly. """ class TransactionError(Exception): """An error occurred due to normal transaction processing.""" class TransactionFailedError(TransactionError): """Cannot perform an operation on a transaction that previously failed. An attempt was made to commit a transaction, or to join a transaction, but this transaction previously raised an exception during an attempt to commit it. The transaction must be explicitly aborted, either by invoking abort() on the transaction, or begin() on its transaction manager. """ class DoomedTransaction(TransactionError): """A commit was attempted on a transaction that was doomed.""" class TransientError(TransactionError): """An error has occured when performing a transaction. It's possible that retrying the transaction will succeed. """ q#}q$(XITransaction.commitq%Xdefq&KmKtq'X!ITransaction.getBeforeCommitHooksq(Xdefq)KKq*XIDataManagerSavepoint.rollbackq+Xdefq,MMq-XITransaction.noteq.Xdefq/KKq0XIDataManagerSavepointq1Xclassq2MMq3XIDataManager.tpc_voteq4Xdefq5MhMrq6XITransaction.savepointq7Xdefq8KKq9X ITransaction.getAfterCommitHooksq:Xdefq;MMqMrMq?XTransientErrorq@XclassqAMMqBXISynchronizer.afterCompletionqCXdefqDMMqEXInvalidSavepointRollbackErrorqFXclassqGMMqHXITransactionManager.savepointqIXdefqJK4K>qKXITransactionManager.abortqLXdefqMK(K,qNXIDataManager.sortKeyqOXdefqPMMqQX ISynchronizerqRXclassqSMMqTXIDataManager.abortqUXdefqVMAMUqWXITransactionManager.getqXXdefqYK K$qZXITransactionManagerq[Xclassq\KKLq]XITransactionDeprecatedq^Xclassq_MM-q`XITransaction.doomqaXdefqbK{KqcX#ITransactionManager.unregisterSynchqdXdefqeKEKLqfXTransactionFailedErrorqgXclassqhMMqiXISynchronizer.newTransactionqjXdefqkMMqlX ISavepointqmXclassqnMMqoXITransactionDeprecated.registerqpXdefqqM)M-qrXIDataManager.tpc_beginqsXdefqtMUM\quX IDataManagerqvXclassqwM-MqxXITransactionManager.beginqyXdefqzKK q{XITransaction.joinq|Xdefq}KKq~XDoomedTransactionqXclassqMMqX ITransaction.addBeforeCommitHookqXdefqKKqXITransactionDeprecated.beginqXdefqM!M)qXITransaction.beforeCommitHookqXdefqKKԇqXISynchronizer.beforeCompletionqXdefqMMqXISavepointDataManagerqXclassqMMqX!ITransactionManager.registerSynchqXdefqK>KEqXISavepointDataManager.savepointqXdefqMMqXITransactionManager.isDoomedqXdefqK0K4qXITransaction.abortqXdefqKtK{qXITransaction.setExtendedInfoqXdefqKKqXITransactionManager.commitqXdefqK$K(qXIDataManager.tpc_abortqXdefqMMqXIDataManager.commitqXdefqM\MhqX ITransactionqXclassqKLMqXITransaction.setUserqXdefqKKqXITransactionManager.doomqXdefqK,K0qXISavepoint.rollbackqXdefqMMqXITransaction.addAfterCommitHookqXdefqKMqXTransactionErrorqXclassqMMqu}q(XITransaction.commitqh X!ITransaction.getBeforeCommitHooksqh XIDataManagerSavepoint.rollbackqh XITransaction.noteqh XIDataManagerSavepointqh XIDataManager.tpc_voteqh XITransaction.savepointqh X ITransaction.getAfterCommitHooksqh XIDataManager.tpc_finishqh XTransientErrorqh XISynchronizer.afterCompletionqh XInvalidSavepointRollbackErrorqh XITransactionManager.savepointqh XITransactionManager.abortqh XIDataManager.sortKeyqh X ISynchronizerqh XIDataManager.abortqh XITransactionManager.getqh XITransactionManagerqh XITransaction.doomqh X#ITransactionManager.unregisterSynchqh XTransactionFailedErrorqh X ISavepointqh XISynchronizer.newTransactionqh XIDataManager.tpc_beginqh X IDataManagerqh XITransactionManager.beginqh XITransaction.joinqh XDoomedTransactionqh X ITransaction.addBeforeCommitHookqh XITransaction.beforeCommitHookqh XISynchronizer.beforeCompletionqh XISavepointDataManagerqh X!ITransactionManager.registerSynchqh XISavepointDataManager.savepointqh XITransactionManager.isDoomedqh XITransaction.abortqh XITransaction.setExtendedInfoqh XITransactionManager.commitqh XIDataManager.tpc_abortqh XIDataManager.commitqh X ITransactionqh XITransaction.setUserqh XITransactionManager.doomqh XISavepoint.rollbackqh XITransaction.addAfterCommitHookqh XTransactionErrorqh uqhXtransaction._managerq셁q}qbX############################################################################ # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################ """A TransactionManager controls transaction boundaries. It coordinates application code and resource managers, so that they are associated with the right transaction. """ import sys import threading from zope.interface import implementer from transaction.interfaces import ITransactionManager from transaction.interfaces import TransientError from transaction.weakset import WeakSet from transaction._compat import reraise from transaction._transaction import Transaction # We have to remember sets of synch objects, especially Connections. # But we don't want mere registration with a transaction manager to # keep a synch object alive forever; in particular, it's common # practice not to explicitly close Connection objects, and keeping # a Connection alive keeps a potentially huge number of other objects # alive (e.g., the cache, and everything reachable from it too). # Therefore we use "weak sets" internally. # Call the ISynchronizer newTransaction() method on every element of # WeakSet synchs. # A transaction manager needs to do this whenever begin() is called. # Since it would be good if tm.get() returned the new transaction while # newTransaction() is running, calling this has to be delayed until after # the transaction manager has done whatever it needs to do to make its # get() return the new txn. def _new_transaction(txn, synchs): if synchs: synchs.map(lambda s: s.newTransaction(txn)) # Important: we must always pass a WeakSet (even if empty) to the Transaction # constructor: synchronizers are registered with the TM, but the # ISynchronizer xyzCompletion() methods are called by Transactions without # consulting the TM, so we need to pass a mutable collection of synchronizers # so that Transactions "see" synchronizers that get registered after the # Transaction object is constructed. @implementer(ITransactionManager) class TransactionManager(object): def __init__(self): self._txn = None self._synchs = WeakSet() def begin(self): """ See ITransactionManager. """ if self._txn is not None: self._txn.abort() txn = self._txn = Transaction(self._synchs, self) _new_transaction(txn, self._synchs) return txn __enter__ = lambda self: self.begin() def get(self): """ See ITransactionManager. """ if self._txn is None: self._txn = Transaction(self._synchs, self) return self._txn def free(self, txn): if txn is not self._txn: raise ValueError("Foreign transaction") self._txn = None def registerSynch(self, synch): """ See ITransactionManager. """ self._synchs.add(synch) def unregisterSynch(self, synch): """ See ITransactionManager. """ self._synchs.remove(synch) def isDoomed(self): """ See ITransactionManager. """ return self.get().isDoomed() def doom(self): """ See ITransactionManager. """ return self.get().doom() def commit(self): """ See ITransactionManager. """ return self.get().commit() def abort(self): """ See ITransactionManager. """ return self.get().abort() def __exit__(self, t, v, tb): if v is None: self.commit() else: self.abort() def savepoint(self, optimistic=False): """ See ITransactionManager. """ return self.get().savepoint(optimistic) def attempts(self, number=3): if number <= 0: raise ValueError("number must be positive") while number: number -= 1 if number: yield Attempt(self) else: yield self def _retryable(self, error_type, error): if issubclass(error_type, TransientError): return True for dm in self.get()._resources: should_retry = getattr(dm, 'should_retry', None) if (should_retry is not None) and should_retry(error): return True class ThreadTransactionManager(TransactionManager, threading.local): """Thread-aware transaction manager. Each thread is associated with a unique transaction. """ class Attempt(object): def __init__(self, manager): self.manager = manager def _retry_or_raise(self, t, v, tb): retry = self.manager._retryable(t, v) self.manager.abort() if retry: return retry # suppress the exception if necessary reraise(t, v, tb) # otherwise reraise the exception def __enter__(self): return self.manager.__enter__() def __exit__(self, t, v, tb): if v is None: try: self.manager.commit() except: return self._retry_or_raise(*sys.exc_info()) else: return self._retry_or_raise(t, v, tb) q}q(XThreadTransactionManagerqXclassqKKqXTransactionManager.freeqXdefqKSKXqXAttempt.__exit__qXdefqKKqXTransactionManager.isDoomedqXdefqKbKgqXAttempt._retry_or_raiseqXdefqKKqXTransactionManager.attemptsrXdefrKKrX TransactionManager.registerSynchrXdefrKXK]rXTransactionManager.__exit__rXdefrKvK|rX_new_transactionr Xdefr K.K:r XTransactionManager._retryabler Xdefr KKrXTransactionManager.__init__rXdefrK=KArXTransactionManager.doomrXdefrKgKlrXTransactionManager.savepointrXdefrK|KrXAttempt.__init__rXdefrKKrXTransactionManager.beginrXdefrKAKJrXAttempt.__enter__rXdefrKKr XTransactionManager.abortr!Xdefr"KqKvr#X"TransactionManager.unregisterSynchr$Xdefr%K]Kbr&XAttemptr'Xclassr(KKr)XTransactionManager.commitr*Xdefr+KlKqr,XTransactionManager.getr-Xdefr.KLKSr/XTransactionManagerr0Xclassr1K;Kr2u}r3(XTransactionManager.__exit__r4h XTransactionManager.abortr5h XTransactionManager.isDoomedr6h XTransactionManager.commitr7h XTransactionManager.beginr8h XThreadTransactionManagerr9h XTransactionManager.doomr:h XTransactionManager.savepointr;h X"TransactionManager.unregisterSynchr<h XTransactionManager.getr=h XTransactionManagerr>h X TransactionManager.registerSynchr?h ur@hXtransaction._transactionrArB}rCbX[############################################################################ # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################ import binascii import logging import sys import weakref import traceback from zope.interface import implementer from transaction.weakset import WeakSet from transaction.interfaces import TransactionFailedError 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 _marker = object() _TB_BUFFER = None #unittests may hook def _makeTracebackBuffer(): #pragma NO COVER if _TB_BUFFER is not None: return _TB_BUFFER return StringIO() _LOGGER = None #unittests may hook def _makeLogger(): #pragma NO COVER if _LOGGER is not None: return _LOGGER 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: # ACTIVE is the initial state. ACTIVE = "Active" COMMITTING = "Committing" COMMITTED = "Committed" DOOMED = "Doomed" # commit() or commit(True) raised an exception. All further attempts # to commit or join this transaction will raise TransactionFailedError. COMMITFAILED = "Commit failed" @implementer(interfaces.ITransaction, interfaces.ITransactionDeprecated) class Transaction(object): # Assign an index to each savepoint so we can invalidate later savepoints # on rollback. The first index assigned is 1, and it goes up by 1 each # time. _savepoint_index = 0 # If savepoints are used, keep a weak key dict of them. This maps a # savepoint to its index (see above). _savepoint2index = None # Meta data. ._extension is also metadata, but is initialized to an # emtpy dict in __init__. user = "" description = "" def __init__(self, synchronizers=None, manager=None): self.status = Status.ACTIVE # List of resource managers, e.g. MultiObjectResourceAdapters. self._resources = [] # Weak set of synchronizer objects to call. if synchronizers is None: synchronizers = WeakSet() self._synchronizers = synchronizers self._manager = manager # _adapters: Connection/_p_jar -> MultiObjectResourceAdapter[Sub] self._adapters = {} self._voted = {} # id(Connection) -> boolean, True if voted # _voted and other dictionaries use the id() of the resource # manager as a key, because we can't guess whether the actual # resource managers will be safe to use as dict keys. # The user, description, and _extension attributes are accessed # directly by storages, leading underscore notwithstanding. self._extension = {} self.log = _makeLogger() self.log.debug("new transaction") # If a commit fails, the traceback is saved in _failure_traceback. # If another attempt is made to commit, TransactionFailedError is # raised, incorporating this traceback. self._failure_traceback = None # List of (hook, args, kws) tuples added by addBeforeCommitHook(). self._before_commit = [] # List of (hook, args, kws) tuples added by addAfterCommitHook(). self._after_commit = [] def isDoomed(self): """ See ITransaction. """ return self.status is Status.DOOMED def doom(self): """ See ITransaction. """ if self.status is not Status.DOOMED: if self.status is not Status.ACTIVE: # should not doom transactions in the middle, # or after, a commit raise ValueError('non-doomable') self.status = Status.DOOMED # Raise TransactionFailedError, due to commit()/join()/register() # getting called when the current transaction has already suffered # a commit/savepoint failure. def _prior_operation_failed(self): assert self._failure_traceback is not None raise TransactionFailedError("An operation previously failed, " "with traceback:\n\n%s" % self._failure_traceback.getvalue()) def join(self, resource): """ See ITransaction. """ if self.status is Status.COMMITFAILED: self._prior_operation_failed() # doesn't return if (self.status is not Status.ACTIVE and self.status is not Status.DOOMED): # TODO: Should it be possible to join a committing transaction? # 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: # A data manager has joined a transaction *after* a savepoint # was created. A couple of things are different in this case: # # 1. We need to add its savepoint to all previous savepoints. # so that if they are rolled back, we roll this one back too. # # 2. We don't actually need to ask the data manager for a # savepoint: because it's just joining, we can just abort it to # roll back to the current state, so we simply use an # AbortSavepoint. datamanager_savepoint = AbortSavepoint(resource, self) for transaction_savepoint in self._savepoint2index.keys(): transaction_savepoint._savepoints.append( datamanager_savepoint) def _unjoin(self, resource): # Leave a transaction because a savepoint was rolled back on a resource # that joined later. # Don't use remove. We don't want to assume anything about __eq__. self._resources = [r for r in self._resources if r is not resource] def savepoint(self, optimistic=False): """ See ITransaction. """ if self.status is Status.COMMITFAILED: self._prior_operation_failed() # doesn't return, it raises try: savepoint = Savepoint(self, optimistic, *self._resources) except: self._cleanup(self._resources) self._saveAndRaiseCommitishError() # reraises! if self._savepoint2index is None: self._savepoint2index = weakref.WeakKeyDictionary() self._savepoint_index += 1 self._savepoint2index[savepoint] = self._savepoint_index return savepoint # Remove and invalidate all savepoints we know about with an index # larger than `savepoint`'s. This is what's needed when a rollback # _to_ `savepoint` is done. def _remove_and_invalidate_after(self, savepoint): savepoint2index = self._savepoint2index index = savepoint2index[savepoint] # use list(items()) to make copy to avoid mutating while iterating for savepoint, i in list(savepoint2index.items()): if i > index: savepoint.transaction = None # invalidate del savepoint2index[savepoint] # Invalidate and forget about all savepoints. def _invalidate_all_savepoints(self): for savepoint in self._savepoint2index.keys(): 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 persisent 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. """ if self.status is Status.DOOMED: raise interfaces.DoomedTransaction( 'transaction doomed, cannot commit') if self._savepoint2index: self._invalidate_all_savepoints() if self.status is Status.COMMITFAILED: self._prior_operation_failed() # doesn't return self._callBeforeCommitHooks() self._synchronizers.map(lambda s: s.beforeCompletion(self)) self.status = Status.COMMITTING try: self._commitResources() self.status = Status.COMMITTED except: t = None v = None tb = None try: t, v, tb = self._saveAndGetCommitishError() self._callAfterCommitHooks(status=False) reraise(t, v, tb) finally: del t, v, tb else: if self._manager: self._manager.free(self) self._synchronizers.map(lambda s: s.afterCompletion(self)) self._callAfterCommitHooks(status=True) self.log.debug("commit") def _saveAndGetCommitishError(self): self.status = Status.COMMITFAILED # Save the traceback for TransactionFailedError. ft = self._failure_traceback = _makeTracebackBuffer() t = None v = None tb = None try: t, v, tb = sys.exc_info() # Record how we got into commit(). traceback.print_stack(sys._getframe(1), None, ft) # Append the stack entries from here down to the exception. traceback.print_tb(tb, None, ft) # Append the exception type and value. ft.writelines(traceback.format_exception_only(t, v)) return t, v, tb finally: del t, v, tb def _saveAndRaiseCommitishError(self): t = None v = None tb = None try: t, v, tb = self._saveAndGetCommitishError() reraise(t, v, tb) finally: del t, v, tb def getBeforeCommitHooks(self): """ See ITransaction. """ return iter(self._before_commit) def addBeforeCommitHook(self, hook, args=(), kws=None): """ See ITransaction. """ if kws is None: kws = {} self._before_commit.append((hook, tuple(args), kws)) def _callBeforeCommitHooks(self): # Call all hooks registered, allowing further registrations # during processing. Note that calls to addBeforeCommitHook() may # add additional hooks while hooks are running, and iterating over a # growing list is well-defined in Python. for hook, args, kws in self._before_commit: hook(*args, **kws) self._before_commit = [] def getAfterCommitHooks(self): """ See ITransaction. """ return iter(self._after_commit) def addAfterCommitHook(self, hook, args=(), kws=None): """ See ITransaction. """ if kws is None: kws = {} self._after_commit.append((hook, tuple(args), kws)) def _callAfterCommitHooks(self, status=True): # Avoid to abort anything at the end if no hooks are registred. if not self._after_commit: return # Call all hooks registered, allowing further registrations # during processing. Note that calls to addAterCommitHook() may # add additional hooks while hooks are running, and iterating over a # growing list is well-defined in Python. for hook, args, kws in self._after_commit: # The first argument passed to the hook is a Boolean value, # true if the commit succeeded, or false if the commit aborted. try: hook(status, *args, **kws) except: # We need to catch the exceptions if we want all hooks # to be called self.log.error("Error in after commit hook exec in %s ", hook, exc_info=sys.exc_info()) # The transaction is already committed. It must not have # further effects after the commit. for rm in self._resources: try: rm.abort(self) except: # XXX should we take further actions here ? self.log.error("Error in abort() on manager %s", rm, exc_info=sys.exc_info()) self._after_commit = [] self._before_commit = [] def _commitResources(self): # Execute the two-phase commit protocol. L = list(self._resources) L.sort(key=rm_key) try: for rm in L: rm.tpc_begin(self) for rm in L: rm.commit(self) self.log.debug("commit %r" % rm) for rm in L: rm.tpc_vote(self) self._voted[id(rm)] = True try: for rm in L: rm.tpc_finish(self) except: # TODO: do we need to make this warning stronger? # TODO: It would be nice if the system could be configured # to stop committing transactions at this point. self.log.critical("A storage error occurred during the second " "phase of the two-phase commit. Resources " "may be in an inconsistent state.") raise except: # If an error occurs committing a transaction, we try # to revert the changes in each of the resource managers. t, v, tb = sys.exc_info() try: try: self._cleanup(L) finally: self._synchronizers.map(lambda s: s.afterCompletion(self)) reraise(t, v, tb) finally: del t, v, tb def _cleanup(self, L): # Called when an exception occurs during tpc_vote or tpc_finish. for rm in L: if id(rm) not in self._voted: try: rm.abort(self) except Exception: self.log.error("Error in abort() on manager %s", rm, exc_info=sys.exc_info()) for rm in L: try: rm.tpc_abort(self) except Exception: self.log.error("Error in tpc_abort() on manager %s", rm, exc_info=sys.exc_info()) def abort(self): """ See ITransaction. """ if self._savepoint2index: self._invalidate_all_savepoints() self._synchronizers.map(lambda s: s.beforeCompletion(self)) try: t = None v = None tb = None for rm in self._resources: try: rm.abort(self) except: if tb is None: t, v, tb = sys.exc_info() self.log.error("Failed to abort resource manager: %s", rm, exc_info=sys.exc_info()) if self._manager: self._manager.free(self) self._synchronizers.map(lambda s: s.afterCompletion(self)) self.log.debug("abort") if tb is not None: reraise(t, v, tb) finally: del t, v, tb def note(self, text): """ See ITransaction. """ text = text.strip() if self.description: self.description += "\n" + text else: self.description = text def setUser(self, user_name, path="/"): """ See ITransaction. """ self.user = "%s %s" % (path, user_name) def setExtendedInfo(self, name, value): """ See ITransaction. """ self._extension[name] = value # 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 mananger 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: """Transaction savepoint. Transaction savepoints coordinate savepoints for data managers participating in a transaction. """ valid = property(lambda self: self.transaction is not None) def __init__(self, transaction, optimistic, *resources): self.transaction = transaction self._savepoints = savepoints = [] for datamanager in resources: try: savepoint = datamanager.savepoint except AttributeError: if not optimistic: raise TypeError("Savepoints unsupported", datamanager) savepoint = NoRollbackSavepoint(datamanager) else: savepoint = savepoint() savepoints.append(savepoint) def rollback(self): """ See ISavepoint. """ transaction = self.transaction if transaction is None: raise interfaces.InvalidSavepointRollbackError( 'invalidated by a later savepoint') transaction._remove_and_invalidate_after(self) try: for savepoint in self._savepoints: savepoint.rollback() except: # Mark the transaction as failed. transaction._saveAndRaiseCommitishError() # reraises! class AbortSavepoint: def __init__(self, datamanager, transaction): self.datamanager = datamanager self.transaction = transaction def rollback(self): self.datamanager.abort(self.transaction) self.transaction._unjoin(self.datamanager) class NoRollbackSavepoint: def __init__(self, datamanager): self.datamanager = datamanager def rollback(self): raise TypeError("Savepoints unsupported", self.datamanager) rD}rE(X&Transaction._invalidate_all_savepointsrFXdefrGKKrHX object_hintrIXdefrJM-M<rKXTransaction.setExtendedInforLXdefrMMMrNX$MultiObjectResourceAdapter.tpc_abortrOXdefrPMM rQXTransaction._unjoinrRXdefrSKKrTX'Transaction._saveAndRaiseCommitishErrorrUXdefrVM5M?rWXAbortSavepointrXXclassrYMMrZXStatusr[Xclassr\K3K@r]X%MultiObjectResourceAdapter.tpc_finishr^Xdefr_MMr`XNoRollbackSavepoint.__init__raXdefrbMMrcX TransactionrdXclassreKBMrfX(Transaction._remove_and_invalidate_afterrgXdefrhKK܇riXDataManagerAdapterrjXclassrkMLMwrlXDataManagerAdapter.tpc_abortrmXdefrnMjMmroXTransaction.addBeforeCommitHookrpXdefrqMDMKrrXTransaction.setUserrsXdefrtMMruXDataManagerAdapter.tpc_votervXdefrwMpMsrxXMultiObjectResourceAdapterryXclassrzMM(r{X"MultiObjectResourceAdapter.sortKeyr|Xdefr}MMr~XAbortSavepoint.rollbackrXdefrMMrX#Transaction._prior_operation_failedrXdefrKKrX!MultiObjectResourceAdapter.commitrXdefrM MrXTransaction.registerrXdefrKKrX#MultiObjectResourceAdapter.__repr__rXdefrMMrXNoRollbackSavepointrXclassrMMrX Transaction.getBeforeCommitHooksrXdefrM?MDrXTransaction.addAfterCommitHookrXdefrMYM`rXDataManagerAdapter.tpc_beginrXdefrMeMjrXAbortSavepoint.__init__rXdefrMMrX!Transaction._callAfterCommitHooksrXdefrM`M~rXTransaction._cleanuprXdefrMMrXSavepoint.__init__rXdefrMMrXSavepoint.rollbackrXdefrMMrXTransaction.noterXdefrMMrX%Transaction._saveAndGetCommitishErrorrXdefrM"M5rXrm_keyrXdefrM(M-rXTransaction.commitrXdefrKM"rX#MultiObjectResourceAdapter.tpc_voterXdefrMMrXDataManagerAdapter.abortrXdefrMbMerX MultiObjectResourceAdapter.abortrXdefrMM(rX _makeLoggerrXdefrK(K0rXDataManagerAdapter.__init__rXdefrMXM]rXTransaction.getAfterCommitHooksrXdefrMTMYrXDataManagerAdapter.tpc_finishrXdefrMmMprXTransaction._commitResourcesrXdefrM~MrXTransaction.doomrXdefrK~KrXNoRollbackSavepoint.rollbackrXdefrMMrXTransaction.joinrXdefrKKrX#MultiObjectResourceAdapter.__init__rXdefrMMrX_makeTracebackBufferrXdefrK"K'rXoid_reprrXdefrM<MLrX"Transaction._callBeforeCommitHooksrXdefrMKMTrXDataManagerAdapter.sortKeyrXdefrMsMwrXDataManagerAdapter.commitrXdefrM]MbrX SavepointrXclassrMxMrXTransaction.isDoomedrXdefrKyK~rX myhasattrrXdefrK0K3rXTransaction.savepointrXdefrKK҇rXTransaction.abortrXdefrMMrXTransaction.__init__rXdefrKTKyrX$MultiObjectResourceAdapter.tpc_beginrXdefrMMru}r(X Savepointrh XTransaction.setUserrh XTransaction.setExtendedInforh XTransaction.registerrh XTransaction.commitrh XSavepoint.rollbackrh X Transaction.getBeforeCommitHooksrh XTransaction.addAfterCommitHookrh XTransaction.getAfterCommitHooksrh XTransaction.noterh XTransaction.isDoomedrh XTransaction.doomr h X Transactionr h XTransaction.savepointr h XTransaction.abortr h XTransaction.addBeforeCommitHookr h XTransaction.joinrh uruUapprNU_nitpick_ignorerh]RrUmetadatar}r(h}rh}rh}rh }rh }rh }rh }rh }ruU indexentriesr}r(h]rh]r h]r!h ]r"h ]r#h ]r$h ]r%((Usingler&Xtransaction.interfaces (module)Xmodule-transaction.interfacesUtr'(j&X9ITransactionManager (interface in transaction.interfaces)X*transaction.interfaces.ITransactionManagerr(Utr)(j&X;begin() (transaction.interfaces.ITransactionManager method)X0transaction.interfaces.ITransactionManager.beginr*Utr+(j&X9get() (transaction.interfaces.ITransactionManager method)X.transaction.interfaces.ITransactionManager.getr,Utr-(j&X<commit() (transaction.interfaces.ITransactionManager method)X1transaction.interfaces.ITransactionManager.commitr.Utr/(j&X;abort() (transaction.interfaces.ITransactionManager method)X0transaction.interfaces.ITransactionManager.abortr0Utr1(j&X:doom() (transaction.interfaces.ITransactionManager method)X/transaction.interfaces.ITransactionManager.doomr2Utr3(j&X>isDoomed() (transaction.interfaces.ITransactionManager method)X3transaction.interfaces.ITransactionManager.isDoomedr4Utr5(j&X?savepoint() (transaction.interfaces.ITransactionManager method)X4transaction.interfaces.ITransactionManager.savepointr6Utr7(j&XCregisterSynch() (transaction.interfaces.ITransactionManager method)X8transaction.interfaces.ITransactionManager.registerSynchr8Utr9(j&XEunregisterSynch() (transaction.interfaces.ITransactionManager method)X:transaction.interfaces.ITransactionManager.unregisterSynchr:Utr;(j&X2ITransaction (interface in transaction.interfaces)X#transaction.interfaces.ITransactionr<Utr=(j&X4user (transaction.interfaces.ITransaction attribute)X(transaction.interfaces.ITransaction.userr>Utr?(j&X;description (transaction.interfaces.ITransaction attribute)X/transaction.interfaces.ITransaction.descriptionr@UtrA(j&X5commit() (transaction.interfaces.ITransaction method)X*transaction.interfaces.ITransaction.commitrBUtrC(j&X4abort() (transaction.interfaces.ITransaction method)X)transaction.interfaces.ITransaction.abortrDUtrE(j&X3doom() (transaction.interfaces.ITransaction method)X(transaction.interfaces.ITransaction.doomrFUtrG(j&X8savepoint() (transaction.interfaces.ITransaction method)X-transaction.interfaces.ITransaction.savepointrHUtrI(j&X3join() (transaction.interfaces.ITransaction method)X(transaction.interfaces.ITransaction.joinrJUtrK(j&X3note() (transaction.interfaces.ITransaction method)X(transaction.interfaces.ITransaction.noterLUtrM(j&X6setUser() (transaction.interfaces.ITransaction method)X+transaction.interfaces.ITransaction.setUserrNUtrO(j&X>setExtendedInfo() (transaction.interfaces.ITransaction method)X3transaction.interfaces.ITransaction.setExtendedInforPUtrQ(j&X?beforeCommitHook() (transaction.interfaces.ITransaction method)X4transaction.interfaces.ITransaction.beforeCommitHookrRUtrS(j&XBaddBeforeCommitHook() (transaction.interfaces.ITransaction method)X7transaction.interfaces.ITransaction.addBeforeCommitHookrTUtrU(j&XCgetBeforeCommitHooks() (transaction.interfaces.ITransaction method)X8transaction.interfaces.ITransaction.getBeforeCommitHooksrVUtrW(j&XAaddAfterCommitHook() (transaction.interfaces.ITransaction method)X6transaction.interfaces.ITransaction.addAfterCommitHookrXUtrY(j&XBgetAfterCommitHooks() (transaction.interfaces.ITransaction method)X7transaction.interfaces.ITransaction.getAfterCommitHooksrZUtr[(j&X2IDataManager (interface in transaction.interfaces)X#transaction.interfaces.IDataManagerr\Utr](j&XCtransaction_manager (transaction.interfaces.IDataManager attribute)X7transaction.interfaces.IDataManager.transaction_managerr^Utr_(j&X4abort() (transaction.interfaces.IDataManager method)X)transaction.interfaces.IDataManager.abortr`Utra(j&X8tpc_begin() (transaction.interfaces.IDataManager method)X-transaction.interfaces.IDataManager.tpc_beginrbUtrc(j&X5commit() (transaction.interfaces.IDataManager method)X*transaction.interfaces.IDataManager.commitrdUtre(j&X7tpc_vote() (transaction.interfaces.IDataManager method)X,transaction.interfaces.IDataManager.tpc_voterfUtrg(j&X9tpc_finish() (transaction.interfaces.IDataManager method)X.transaction.interfaces.IDataManager.tpc_finishrhUtri(j&X8tpc_abort() (transaction.interfaces.IDataManager method)X-transaction.interfaces.IDataManager.tpc_abortrjUtrk(j&X6sortKey() (transaction.interfaces.IDataManager method)X+transaction.interfaces.IDataManager.sortKeyrlUtrm(j&X;ISavepointDataManager (interface in transaction.interfaces)X,transaction.interfaces.ISavepointDataManagerrnUtro(j&XAsavepoint() (transaction.interfaces.ISavepointDataManager method)X6transaction.interfaces.ISavepointDataManager.savepointrpUtrq(j&X;IDataManagerSavepoint (interface in transaction.interfaces)X,transaction.interfaces.IDataManagerSavepointrrUtrs(j&X@rollback() (transaction.interfaces.IDataManagerSavepoint method)X5transaction.interfaces.IDataManagerSavepoint.rollbackrtUtru(j&X0ISavepoint (interface in transaction.interfaces)X!transaction.interfaces.ISavepointrvUtrw(j&X5rollback() (transaction.interfaces.ISavepoint method)X*transaction.interfaces.ISavepoint.rollbackrxUtry(j&X3valid (transaction.interfaces.ISavepoint attribute)X'transaction.interfaces.ISavepoint.validrzUtr{(j&X?InvalidSavepointRollbackError (class in transaction.interfaces)X4transaction.interfaces.InvalidSavepointRollbackErrorr|Utr}(j&X3ISynchronizer (interface in transaction.interfaces)X$transaction.interfaces.ISynchronizerr~Utr(j&X@beforeCompletion() (transaction.interfaces.ISynchronizer method)X5transaction.interfaces.ISynchronizer.beforeCompletionrUtr(j&X?afterCompletion() (transaction.interfaces.ISynchronizer method)X4transaction.interfaces.ISynchronizer.afterCompletionrUtr(j&X>newTransaction() (transaction.interfaces.ISynchronizer method)X3transaction.interfaces.ISynchronizer.newTransactionrUtr(j&X2TransactionError (class in transaction.interfaces)X'transaction.interfaces.TransactionErrorrUtr(j&X8TransactionFailedError (class in transaction.interfaces)X-transaction.interfaces.TransactionFailedErrorrUtr(j&X3DoomedTransaction (class in transaction.interfaces)X(transaction.interfaces.DoomedTransactionrUtr(j&X0TransientError (class in transaction.interfaces)X%transaction.interfaces.TransientErrorrUtr(j&X!transaction._transaction (module)Xmodule-transaction._transactionUtr(j&X/Transaction (class in transaction._transaction)X$transaction._transaction.TransactionrUtr(j&X8isDoomed() (transaction._transaction.Transaction method)X-transaction._transaction.Transaction.isDoomedrUtr(j&X4doom() (transaction._transaction.Transaction method)X)transaction._transaction.Transaction.doomrUtr(j&X4join() (transaction._transaction.Transaction method)X)transaction._transaction.Transaction.joinrUtr(j&X9savepoint() (transaction._transaction.Transaction method)X.transaction._transaction.Transaction.savepointrUtr(j&X8register() (transaction._transaction.Transaction method)X-transaction._transaction.Transaction.registerrUtr(j&X6commit() (transaction._transaction.Transaction method)X+transaction._transaction.Transaction.commitrUtr(j&XDgetBeforeCommitHooks() (transaction._transaction.Transaction method)X9transaction._transaction.Transaction.getBeforeCommitHooksrUtr(j&XCaddBeforeCommitHook() (transaction._transaction.Transaction method)X8transaction._transaction.Transaction.addBeforeCommitHookrUtr(j&XCgetAfterCommitHooks() (transaction._transaction.Transaction method)X8transaction._transaction.Transaction.getAfterCommitHooksrUtr(j&XBaddAfterCommitHook() (transaction._transaction.Transaction method)X7transaction._transaction.Transaction.addAfterCommitHookrUtr(j&X5abort() (transaction._transaction.Transaction method)X*transaction._transaction.Transaction.abortrUtr(j&X4note() (transaction._transaction.Transaction method)X)transaction._transaction.Transaction.noterUtr(j&X7setUser() (transaction._transaction.Transaction method)X,transaction._transaction.Transaction.setUserrUtr(j&X?setExtendedInfo() (transaction._transaction.Transaction method)X4transaction._transaction.Transaction.setExtendedInforUtr(j&X-Savepoint (class in transaction._transaction)X"transaction._transaction.SavepointrUtr(j&X6rollback() (transaction._transaction.Savepoint method)X+transaction._transaction.Savepoint.rollbackrUtr(j&Xtransaction._manager (module)Xmodule-transaction._managerUtr(j&X2TransactionManager (class in transaction._manager)X'transaction._manager.TransactionManagerrUtr(j&X<__enter__() (transaction._manager.TransactionManager method)X1transaction._manager.TransactionManager.__enter__rUtr(j&X;__exit__() (transaction._manager.TransactionManager method)X0transaction._manager.TransactionManager.__exit__rUtr(j&X8begin() (transaction._manager.TransactionManager method)X-transaction._manager.TransactionManager.beginrUtr(j&X6get() (transaction._manager.TransactionManager method)X+transaction._manager.TransactionManager.getrUtr(j&X@registerSynch() (transaction._manager.TransactionManager method)X5transaction._manager.TransactionManager.registerSynchrUtr(j&XBunregisterSynch() (transaction._manager.TransactionManager method)X7transaction._manager.TransactionManager.unregisterSynchrUtr(j&X;isDoomed() (transaction._manager.TransactionManager method)X0transaction._manager.TransactionManager.isDoomedrUtr(j&X7doom() (transaction._manager.TransactionManager method)X,transaction._manager.TransactionManager.doomrUtr(j&X9commit() (transaction._manager.TransactionManager method)X.transaction._manager.TransactionManager.commitrUtr(j&X8abort() (transaction._manager.TransactionManager method)X-transaction._manager.TransactionManager.abortrUtr(j&X<savepoint() (transaction._manager.TransactionManager method)X1transaction._manager.TransactionManager.savepointrUtr(j&X8ThreadTransactionManager (class in transaction._manager)X-transaction._manager.ThreadTransactionManagerrUtreh ]ruUtoctree_includesr}rh ]r(X conveniencerXdoomrX savepointrXhooksrX datamanagerrXresourcemanagerrXapiresUtocsr}r(hcdocutils.nodes bullet_list r)r}r(UtagnamerU bullet_listrUchildrenr]rcdocutils.nodes list_item r)r}r(UparentrjjU list_itemrj]r(csphinx.addnodes compact_paragraph r)r}r(jjjUcompact_paragraphrj]rcdocutils.nodes reference r)r}r(jjjU referencerj]rcdocutils.nodes Text rXTransaction convenience supportrr}r(jjU rawsourcerXTransaction convenience supportrubajUU attributesr}r(U anchornamerUUidsr]rUdupnamesr]rUnamesr]rUbackrefsr]rUclassesr]rUrefurirhUinternalruubajUj}r(j]rj]rj]rj]r j]r uubj)r }r (jjjjj]r (j)r}r(jj jjj]rj)r}r(jjjjj]rj)r}r(jjjjj]rjX with supportrr}r(jjjX with supportrubajUj}r(U anchornamerU #with-supportrj]rj]rj]r j]r!j]r"Urefurir#hUinternalr$uubajUj}r%(j]r&j]r'j]r(j]r)j]r*uubajUj}r+(j]r,j]r-j]r.j]r/j]r0uubj)r1}r2(jj jjj]r3j)r4}r5(jj1jjj]r6j)r7}r8(jj4jjj]r9jXRetriesr:r;}r<(jj7jXRetriesr=ubajUj}r>(U anchornamer?U#retriesr@j]rAj]rBj]rCj]rDj]rEUrefurirFhUinternalrGuubajUj}rH(j]rIj]rJj]rKj]rLj]rMuubajUj}rN(j]rOj]rPj]rQj]rRj]rSuubejUj}rT(j]rUj]rVj]rWj]rXj]rYuubejUj}rZ(j]r[j]r\j]r]j]r^j]r_uubajUj}r`(j]raj]rbj]rcj]rdj]reuubhj)rf}rg(jjj]rhj)ri}rj(jjfjjj]rkj)rl}rm(jjijjj]rnj)ro}rp(jjljjj]rqjXDooming Transactionsrrrs}rt(jjojXDooming TransactionsruubajUj}rv(U anchornamerwUj]rxj]ryj]rzj]r{j]r|Urefurir}hUinternalr~uubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubhj)r}r(jjj]rj)r}r(jjjjj]r(j)r}r(jjjjj]rj)r}r(jjjjj]rjX Savepointsrr}r(jjjX SavepointsrubajUj}r(U anchornamerUj]rj]rj]rj]rj]rUrefurirhUinternalruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjjjj]r(j)r}r(jjjjj]rj)r}r(jjjjj]rj)r}r(jjjjj]rjX Applicationsrr}r(jjjX ApplicationsrubajUj}r(U anchornamerU #applicationsrj]rj]rj]rj]rj]rUrefurirhUinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjjjj]rj)r}r(jjjjj]rj)r}r(jjjjj]rjXSavepoint invalidationrr}r(jjjXSavepoint invalidationrubajUj}r(U anchornamerU#savepoint-invalidationrj]rj]rj]rj]rj]rUrefurirhUinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjjjj]rj)r}r(jjjjj]rj)r}r(jjjjj]rjX#Databases without savepoint supportrr}r(jjjX#Databases without savepoint supportrubajUj}r(U anchornamerU$#databases-without-savepoint-supportrj]r j]r j]r j]r j]r UrefurirhUinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjjjj]rj)r}r (jjjjj]r!j)r"}r#(jjjjj]r$jXFailuresr%r&}r'(jj"jXFailuresr(ubajUj}r)(U anchornamer*U #failuresr+j]r,j]r-j]r.j]r/j]r0Urefurir1hUinternalr2uubajUj}r3(j]r4j]r5j]r6j]r7j]r8uubajUj}r9(j]r:j]r;j]r<j]r=j]r>uubejUj}r?(j]r@j]rAj]rBj]rCj]rDuubejUj}rE(j]rFj]rGj]rHj]rIj]rJuubajUj}rK(j]rLj]rMj]rNj]rOj]rPuubh j)rQ}rR(jjj]rSj)rT}rU(jjQjjj]rV(j)rW}rX(jjTjjj]rYj)rZ}r[(jjWjjj]r\jXWriting a Resource Managerr]r^}r_(jjZjXWriting a Resource Managerr`ubajUj}ra(U anchornamerbUj]rcj]rdj]rej]rfj]rgUrefurirhh UinternalriuubajUj}rj(j]rkj]rlj]rmj]rnj]rouubj)rp}rq(jjTjjj]rr(j)rs}rt(jjpjjj]ruj)rv}rw(jjsjjj]rxj)ry}rz(jjvjjj]r{jXSimple Resource Managerr|r}}r~(jjyjXSimple Resource ManagerrubajUj}r(U anchornamerU#simple-resource-managerrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjpjjj]rj)r}r(jjjjj]rj)r}r(jjjjj]r(jXThe rr}r(jjjXThe rubcdocutils.nodes literal r)r}r(jjjUliteralrj]rjX tpc_begin()rr}r(jjjUubajX tpc_begin()rj}r(j]rj]rj]rj]rj]ruubjX Methodrr}r(jjjX MethodrubejUj}r(U anchornamerU#the-tpc-begin-methodrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjpjjj]rj)r}r(jjjjj]rj)r}r(jjjjj]r(jXThe rr}r(jjjXThe rubj)r}r(jjjjj]rjX tpc_vote()rr}r(jjjUubajX tpc_vote()rj}r(j]rj]rj]rj]rj]ruubjX Methodrr}r(jjjX MethodrubejUj}r(U anchornamerU#the-tpc-vote-methodrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjpjjj]rj)r}r(jjjjj]rj)r}r(jjjjj]r(jXThe r r }r (jjjXThe r ubj)r }r(jjjjj]rjX tpc_finish()rr}r(jj jUubajX tpc_finish()rj}r(j]rj]rj]rj]rj]ruubjX Methodrr}r(jjjX MethodrubejUj}r(U anchornamerU#the-tpc-finish-methodr j]r!j]r"j]r#j]r$j]r%Urefurir&h Uinternalr'uubajUj}r((j]r)j]r*j]r+j]r,j]r-uubajUj}r.(j]r/j]r0j]r1j]r2j]r3uubj)r4}r5(jjpjjj]r6j)r7}r8(jj4jjj]r9j)r:}r;(jj7jjj]r<(jXThe r=r>}r?(jj:jXThe r@ubj)rA}rB(jj:jjj]rCjX tpc_abort()rDrE}rF(jjAjUubajX tpc_abort()rGj}rH(j]rIj]rJj]rKj]rLj]rMuubjX MethodrNrO}rP(jj:jX MethodrQubejUj}rR(U anchornamerSU#the-tpc-abort-methodrTj]rUj]rVj]rWj]rXj]rYUrefurirZh Uinternalr[uubajUj}r\(j]r]j]r^j]r_j]r`j]rauubajUj}rb(j]rcj]rdj]rej]rfj]rguubj)rh}ri(jjpjjj]rjj)rk}rl(jjhjjj]rmj)rn}ro(jjkjjj]rp(jXThe rqrr}rs(jjnjXThe rtubj)ru}rv(jjnjjj]rwjX savepoint()rxry}rz(jjujUubajX savepoint()r{j}r|(j]r}j]r~j]rj]rj]ruubjX Methodrr}r(jjnjX MethodrubejUj}r(U anchornamerU#the-savepoint-methodrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubejUj}r(j]rj]rj]rj]rj]ruubejUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubh j)r}r(jjj]rj)r}r(jjjjj]r(j)r}r(jjjjj]rj)r}r(jjjjj]rjX!Hooking the Transaction Machineryrr}r(jjjX!Hooking the Transaction MachineryrubajUj}r(U anchornamerUj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjjjj]r(j)r}r(jjjjj]rj)r}r(jjjjj]rj)r}r(jjjjj]r(jXThe rr}r(jjjXThe rubj)r}r(jjjjj]rjXaddBeforeCommitHook()rr}r(jjjUubajXaddBeforeCommitHook()rj}r(j]rj]rj]rj]rj]ruubjX Methodrr}r(jjjX MethodrubejUj}r(U anchornamerU#the-addbeforecommithook-methodrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjjjj]rj)r}r(jjjjj]r j)r }r (jjjjj]r (jXThe r r}r(jj jXThe rubj)r}r(jj jjj]rjXaddAfterCommitHook()rr}r(jjjUubajXaddAfterCommitHook()rj}r(j]rj]rj]rj]rj]ruubjX Methodrr}r (jj jX Methodr!ubejUj}r"(U anchornamer#U#the-addaftercommithook-methodr$j]r%j]r&j]r'j]r(j]r)Urefurir*h Uinternalr+uubajUj}r,(j]r-j]r.j]r/j]r0j]r1uubajUj}r2(j]r3j]r4j]r5j]r6j]r7uubejUj}r8(j]r9j]r:j]r;j]r<j]r=uubejUj}r>(j]r?j]r@j]rAj]rBj]rCuubajUj}rD(j]rEj]rFj]rGj]rHj]rIuubh j)rJ}rK(jjj]rLj)rM}rN(jjJjjj]rO(j)rP}rQ(jjMjjj]rRj)rS}rT(jjPjjj]rUjXWriting a Data ManagerrVrW}rX(jjSjXWriting a Data ManagerrYubajUj}rZ(U anchornamer[Uj]r\j]r]j]r^j]r_j]r`Urefurirah UinternalrbuubajUj}rc(j]rdj]rej]rfj]rgj]rhuubj)ri}rj(jjMjjj]rk(j)rl}rm(jjijjj]rnj)ro}rp(jjljjj]rqj)rr}rs(jjojjj]rtjXSimple Data Managerrurv}rw(jjrjXSimple Data ManagerrxubajUj}ry(U anchornamerzU#simple-data-managerr{j]r|j]r}j]r~j]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjijjj]rj)r}r(jjjjj]rj)r}r(jjjjj]r(jXThe rr}r(jjjXThe rubj)r}r(jjjjj]rjX prepare()rr}r(jjjUubajX prepare()rj}r(j]rj]rj]rj]rj]ruubjX Methodrr}r(jjjX MethodrubejUj}r(U anchornamerU#the-prepare-methodrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjijjj]rj)r}r(jjjjj]rj)r}r(jjjjj]r(jXThe rr}r(jjjXThe rubj)r}r(jjjjj]rjXabort()rr}r(jjjUubajXabort()rj}r(j]rj]rj]rj]rj]ruubjX methodrr}r(jjjX methodrubejUj}r(U anchornamerU#the-abort-methodrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjijjj]rj)r}r(jjjjj]rj)r}r(jjjjj]r(jXThe rr}r(jjjXThe rubj)r}r(jjjjj]rjXcommit()rr}r (jjjUubajXcommit()r j}r (j]r j]r j]rj]rj]ruubjX methodrr}r(jjjX methodrubejUj}r(U anchornamerU#the-commit-methodrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]r j]r!j]r"j]r#j]r$uubajUj}r%(j]r&j]r'j]r(j]r)j]r*uubj)r+}r,(jjijjj]r-j)r.}r/(jj+jjj]r0j)r1}r2(jj.jjj]r3(jXThe r4r5}r6(jj1jXThe r7ubj)r8}r9(jj1jjj]r:jX savepoint()r;r<}r=(jj8jUubajX savepoint()r>j}r?(j]r@j]rAj]rBj]rCj]rDuubjX methodrErF}rG(jj1jX methodrHubejUj}rI(U anchornamerJU#the-savepoint-methodrKj]rLj]rMj]rNj]rOj]rPUrefurirQh UinternalrRuubajUj}rS(j]rTj]rUj]rVj]rWj]rXuubajUj}rY(j]rZj]r[j]r\j]r]j]r^uubejUj}r_(j]r`j]raj]rbj]rcj]rduubejUj}re(j]rfj]rgj]rhj]rij]rjuubajUj}rk(j]rlj]rmj]rnj]roj]rpuubh j)rq}rr(jjj]rsj)rt}ru(jjqjjj]rv(j)rw}rx(jjtjjj]ryj)rz}r{(jjwjjj]r|(j)r}}r~(jjzjjj]rjX transactionrr}r(jj}jUubajX transactionrj}r(j]rj]rj]rj]rj]ruubjX API Referencerr}r(jjzjX API ReferencerubejUj}r(U anchornamerUj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjtjjj]r(j)r}r(jjjjj]rj)r}r(jjjjj]rj)r}r(jjjjj]rjX Interfacesrr}r(jjjX InterfacesrubajUj}r(U anchornamerX#module-transaction.interfacesrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjjjj]rj)r}r(jjjjj]rj)r}r(jjjjj]rjX API Objectsrr}r(jjjX API ObjectsrubajUj}r(U anchornamerX #module-transaction._transactionrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubejUj}r(j]rj]rj]rj]rj]ruubejUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubh j)r}r(jjj]r(j)r}r(jjjjj]r(j)r}r(jjjjj]rj)r}r(jjjjj]r(j)r}r(jjjjj]rjX transactionrr}r (jjjUubajX transactionr j}r (j]r j]r j]rj]rj]ruubjX Documentationrr}r(jjjX DocumentationrubejUj}r(U anchornamerUj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]r j]r!j]r"j]r#uubj)r$}r%(jjjjj]r&(j)r'}r((jj$jjj]r)j)r*}r+(jj'jjj]r,j)r-}r.(jj*jjj]r/jXCompatibility issuesr0r1}r2(jj-jXCompatibility issuesr3ubajUj}r4(U anchornamer5U#compatibility-issuesr6j]r7j]r8j]r9j]r:j]r;Urefurir<h Uinternalr=uubajUj}r>(j]r?j]r@j]rAj]rBj]rCuubajUj}rD(j]rEj]rFj]rGj]rHj]rIuubj)rJ}rK(jj$jjj]rLj)rM}rN(jjJjjj]rOj)rP}rQ(jjMjjj]rRjXTwo-phase commitrSrT}rU(jjPjXTwo-phase commitrVubajUj}rW(U anchornamerXU#two-phase-commitrYj]rZj]r[j]r\j]r]j]r^Urefurir_h Uinternalr`uubajUj}ra(j]rbj]rcj]rdj]rej]rfuubajUj}rg(j]rhj]rij]rjj]rkj]rluubj)rm}rn(jj$jjj]roj)rp}rq(jjmjjj]rrj)rs}rt(jjpjjj]rujXBefore-commit hookrvrw}rx(jjsjXBefore-commit hookryubajUj}rz(U anchornamer{U#before-commit-hookr|j]r}j]r~j]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jj$jjj]rj)r}r(jjjjj]rj)r}r(jjjjj]rjXAfter-commit hookrr}r(jjjXAfter-commit hookrubajUj}r(U anchornamerU#after-commit-hookrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jj$jjj]rj)r}r(jjjjj]rj)r}r(jjjjj]rjXError handlingrr}r(jjjXError handlingrubajUj}r(U anchornamerU#error-handlingrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jj$jjj]r(j)r}r(jjjjj]rj)r}r(jjjjj]rjXSynchronizationrr}r(jjjXSynchronizationrubajUj}r(U anchornamerU#synchronizationrj]rj]rj]rj]rj]rUrefurirh UinternalruubajUj}r(j]rj]rj]rj]rj]ruubj)r}r(jjjjj]rcsphinx.addnodes toctree r)r}r(jjjUtoctreerj]rjUj}r(U includehiddenrUhiddenrU titlesonlyrj]rj]r Unumberedr KUmaxdepthr Kj]r Uentriesr ]r (Njr Njr Njr Njr Njr Njr Njr eUglobr j]r j]r Uparentr h U includefilesr ]r (jjjjjjjeuubajUj}r (j]r j]r j]r j]r j]r uubejUj}r (j]r j]r j]r j]r j]r uubejUj}r (j]r j]r! j]r" j]r# j]r$ uubejUj}r% (j]r& j]r' j]r( j]r) j]r* uubj)r+ }r, (jjjjj]r- j)r. }r/ (jj+ jjj]r0 j)r1 }r2 (jj. jjj]r3 jXIndices and tablesr4 r5 }r6 (jj1 jXIndices and tablesr7 ubajUj}r8 (U anchornamer9 U#indices-and-tablesr: j]r; j]r< j]r= j]r> j]r? Urefurir@ h UinternalrA uubajUj}rB (j]rC j]rD j]rE j]rF j]rG uubajUj}rH (j]rI j]rJ j]rK j]rL j]rM uubejUj}rN (j]rO j]rP j]rQ j]rR j]rS uubuUversioning_conditionrT Utoc_num_entriesrU }rV (hKhKhKh Kh Kh Kh Kh KuU domaindatarW }rX (Uc}rY (UversionrZ KUobjectsr[ }r\ uUrstr] }r^ (jZ Kj[ }r_ uUstdr` }ra (jZ KUlabelsrb }rc (Umodindexrd U py-modindexUcsphinx.locale _TranslationProxy re csphinx.locale mygettext rf U Module Indexrg rh jf jg ri brj Ugenindexrk jk Uje jf UIndexrl rm jf jl rn bro Usearchrp jp Uje jf U Search Pagerq rr jf jq rs brt uU progoptionsru }rv U anonlabelsrw }rx (jd U py-modindexUry jk jk Urz jp UsearchUr{ uj[ }r| uUpyr} }r~ (jZ KUmodulesr }r (Xtransaction.interfacesr (h UUtr Xtransaction._managerr (h UUtr Xtransaction._transactionr (h UUtr uj[ }r (jh Xmethodr r jnh X interfacer r jTh Xmethodr r jh Xclassr r jh Xclassr r jzh X attributer r jh Xmethodr r j0h Xmethodr r jfh Xmethodr r jh Xmethodr r jVh Xmethodr r jXh Xmethodr r jh Xmethodr r jh Xmethodr r jvh X interfacer r jh Xclassr r jDh Xmethodr r j~h X interfacer r j h Umoduler r jh Xclassr r jh Xclassr r jh Xmethodr r jh Xmethodr r j h j r j6h Xmethodr r jbh Xmethodr r jh Xmethodr r jFh Xmethodr r jdh Xmethodr r jBh Xmethodr r jh Xmethodr r jhh Xmethodr r j4h Xmethodr r jNh Xmethodr r jh Xmethodr r jh Xclassr r jh Xmethodr r jh Xmethodr r jh Xmethodr r j`h Xmethodr r j<h X interfacer r jZh Xmethodr r j8h Xmethodr r j(h X interfacer r jh Xmethodr r jh Xmethodr r jxh Xmethodr r jHh Xmethodr r jh Xmethodr r jh Xmethodr r j>h X attributer r jrh X interfacer r jh Xmethodr r jLh Xmethodr r jjh Xmethodr r jh Xclassr r j^h X attributer r jh Xmethodr r jh Xmethodr r jlh Xmethodr r j@h X attributer r jPh Xmethodr r j*h Xmethodr r jth Xmethodr r j h j r jh Xmethodr r jh Xmethodr r jh Xmethodr r jh Xmethodr r jh Xclassr r jh Xmethodr r jph Xmethodr r j:h Xmethodr r jJh Xmethodr r j,h Xmethodr r jh Xmethodr r jh Xmethodr r jRh Xmethodr r! j|h Xclassr" r# jh Xmethodr$ r% j2h Xmethodr& r' jh Xmethodr( r) j\h X interfacer* r+ j.h Xmethodr, r- uuUcppr. }r/ (jZ Kj[ }r0 uUjsr1 }r2 (jZ Kj[ }r3 uuUfiles_to_rebuildr4 }r5 (jh]r6 h aRr7 jh]r8 h aRr9 jh]r: h aRr; jh]r< h aRr= jh]r> h aRr? jh]r@ h aRrA jh]rB h aRrC uUsrcdirrD X1/home/tseaver/projects/Zope/ZODB/transaction/docsrE U dependenciesrF }rG h h]rH (U@../.tox/docs/lib/python2.6/site-packages/transaction/_manager.pyrI UD../.tox/docs/lib/python2.6/site-packages/transaction/_transaction.pyrJ UB../.tox/docs/lib/python2.6/site-packages/transaction/interfaces.pyrK eRrL sjZ K*U glob_toctreesrM h]RrN U citationsrO }rP UtitlesrQ }rR (hcdocutils.nodes title rS )rT }rU (jUtitlerV j]rW jXTransaction convenience supportrX rY }rZ (jjT jjubajUj}r[ (j]r\ j]r] j]r^ j]r_ j]r` uubhjS )ra }rb (jjV j]rc jXDooming Transactionsrd re }rf (jja jjuubajUj}rg (j]rh j]ri j]rj j]rk j]rl uubhjS )rm }rn (jjV j]ro jX Savepointsrp rq }rr (jjm jjubajUj}rs (j]rt j]ru j]rv j]rw j]rx uubh jS )ry }rz (jjV j]r{ jXWriting a Resource Managerr| r} }r~ (jjy jj`ubajUj}r (j]r j]r j]r j]r j]r uubh jS )r }r (jjV j]r jX!Hooking the Transaction Machineryr r }r (jj jjubajUj}r (j]r j]r j]r j]r j]r uubh jS )r }r (jjV j]r jXWriting a Data Managerr r }r (jj jjYubajUj}r (j]r j]r j]r j]r j]r uubh jS )r }r (jjV j]r (j)r }r (jj jjj]r jX transactionr r }r (jj jUubajX transactionr j}r (j]r j]r j]r j]r j]r uubjX API Referencer r }r (jj jjubejUj}r (j]r j]r j]r j]r j]r uubh jS )r }r (jjV j]r (j)r }r (jj jjj]r jX transactionr r }r (jj jUubajX transactionr j}r (j]r j]r j]r j]r j]r uubjX Documentationr r }r (jj jjubejUj}r (j]r j]r j]r j]r j]r uubuUtodo_all_todosr ]r U _warnfuncr NUconfigr csphinx.config Config r )r }r (U copyrightr X"2012, Zope Foundation Contributorsr Ulatex_documentsr ]r (Uindexr Utransaction.texXtransaction Documentationr XZope Foundation Contributorsr Umanualtr aUprojectr X transactionr U overridesr }r Utexinfo_documentsr ]r (j U transactionr j j j U One line description of project.U Miscellaneousr tr aUexclude_patternsr ]r U_buildr aUhtmlhelp_basenamer Utransactiondocr U source_suffixr U.rstr Upygments_styler Usphinxr U man_pagesr ]r (Uindexr j j ]r j aKtr aU html_themer Udefaultr U extensionsr ]r (Usphinx.ext.autodocr Usphinx.ext.doctestr Usphinx.ext.todor Usphinx.ext.viewcoder Urepoze.sphinx.autointerfacer eUsetupr NUhtml_static_pathr ]r U_staticr aU master_docr j jZ U1.2r Ulatex_elementsr }r Ureleaser j Utemplates_pathr ]r U _templatesr aubUtoc_secnumbersr }r Usettingsr }r (Usectsubtitle_xformr U rfc_base_urlr Uhttp://tools.ietf.org/html/r Uembed_stylesheetr Ufile_insertion_enabledr U halt_levelr KUinput_encodingr U utf-8-sigr U pep_base_urlr Uhttp://www.python.org/dev/peps/r Ucloak_email_addressesr Udoctitle_xformr Ugettext_compactr Utrim_footnote_reference_spacer Uenvr hUwarning_streamr csphinx.util.nodes WarningStream r )r }r (U_rer! cre _compile r" U+\((DEBUG|INFO|WARNING|ERROR|SEVERE)/[0-4]\)r# KRr$ Uwarnfuncr% NubuUnumbered_toctreesr& h]Rr' U longtitlesr( }r) (hjT hja hjm h jy h j h j h j h j uUversionchangesr* }r+ U reread_alwaysr, h]Rr- ub.transaction-1.4.3/docs/_build/html/0000775000175000017500000000000012312641352017103 5ustar tseavertseavertransaction-1.4.3/docs/_build/html/_sources/0000775000175000017500000000000012312641352020725 5ustar tseavertseavertransaction-1.4.3/docs/_build/html/_sources/doom.txt0000664000175000017500000001050012073316126022422 0ustar tseavertseaverDooming Transactions ==================== A doomed transaction behaves exactly the same way as an active transaction but raises an error on any attempt to commit it, thus forcing an abort. Doom is useful in places where abort is unsafe and an exception cannot be raised. This occurs when the programmer wants the code following the doom to run but not commit. It is unsafe to abort in these circumstances as a following get() may implicitly open a new transaction. Any attempt to commit a doomed transaction will raise a DoomedTransaction exception. An example of such a use case can be found in zope/app/form/browser/editview.py. Here a form validation failure must doom the transaction as committing the transaction may have side-effects. However, the form code must continue to calculate a form containing the error messages to return. For Zope in general, code running within a request should always doom transactions rather than aborting them. It is the responsibilty of the publication to either abort() or commit() the transaction. Application code can use savepoints and doom() safely. To see how it works we first need to create a stub data manager: .. doctest:: >>> from transaction.interfaces import IDataManager >>> from zope.interface import implementer >>> @implementer(IDataManager) ... class DataManager: ... def __init__(self): ... self.attr_counter = {} ... def __getattr__(self, name): ... def f(transaction): ... self.attr_counter[name] = self.attr_counter.get(name, 0) + 1 ... return f ... def total(self): ... count = 0 ... for access_count in self.attr_counter.values(): ... count += access_count ... return count ... def sortKey(self): ... return 1 Start a new transaction: .. doctest:: >>> import transaction >>> txn = transaction.begin() >>> dm = DataManager() >>> txn.join(dm) We can ask a transaction if it is doomed to avoid expensive operations. An example of a use case is an object-relational mapper where a pre-commit hook sends all outstanding SQL to a relational database for objects changed during the transaction. This expensive operation is not necessary if the transaction has been doomed. A non-doomed transaction should return False: .. doctest:: >>> txn.isDoomed() False We can doom a transaction by calling .doom() on it: .. doctest:: >>> txn.doom() >>> txn.isDoomed() True We can doom it again if we like: .. doctest:: >>> txn.doom() The data manager is unchanged at this point: .. doctest:: >>> dm.total() 0 Attempting to commit a doomed transaction any number of times raises a DoomedTransaction: .. doctest:: >>> txn.commit() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): DoomedTransaction: transaction doomed, cannot commit >>> txn.commit() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): DoomedTransaction: transaction doomed, cannot commit But still leaves the data manager unchanged: .. doctest:: >>> dm.total() 0 But the doomed transaction can be aborted: .. doctest:: >>> txn.abort() Which aborts the data manager: .. doctest:: >>> dm.total() 1 >>> dm.attr_counter['abort'] 1 Dooming the current transaction can also be done directly from the transaction module. We can also begin a new transaction directly after dooming the old one: .. doctest:: >>> txn = transaction.begin() >>> transaction.isDoomed() False >>> transaction.doom() >>> transaction.isDoomed() True >>> txn = transaction.begin() After committing a transaction we get an assertion error if we try to doom the transaction. This could be made more specific, but trying to doom a transaction after it's been committed is probably a programming error: .. doctest:: >>> txn = transaction.begin() >>> txn.commit() >>> txn.doom() Traceback (most recent call last): ... ValueError: non-doomable A doomed transaction should act the same as an active transaction, so we should be able to join it: .. doctest:: >>> txn = transaction.begin() >>> txn.doom() >>> dm2 = DataManager() >>> txn.join(dm2) Clean up: .. doctest:: >>> txn = transaction.begin() >>> txn.abort() transaction-1.4.3/docs/_build/html/_sources/api.txt0000664000175000017500000000306412073316126022244 0ustar tseavertseaver:mod:`transaction` API Reference ================================ Interfaces ---------- .. module:: transaction.interfaces .. autointerface:: ITransactionManager :members: :member-order: bysource .. autointerface:: ITransaction :members: :member-order: bysource .. autointerface:: IDataManager :members: :member-order: bysource .. autointerface:: ISavepointDataManager :members: :member-order: bysource .. autointerface:: IDataManagerSavepoint :members: :member-order: bysource .. autointerface:: ISavepoint :members: :member-order: bysource .. autoclass:: InvalidSavepointRollbackError :members: :member-order: bysource .. autointerface:: ISynchronizer :members: :member-order: bysource .. autoclass:: TransactionError :members: :member-order: bysource .. autoclass:: TransactionFailedError :members: :member-order: bysource .. autoclass:: DoomedTransaction :members: :member-order: bysource .. autoclass:: TransientError :members: :member-order: bysource API Objects ----------- .. module:: transaction._transaction .. autoclass:: Transaction :members: :member-order: bysource .. autoclass:: Savepoint :members: :member-order: bysource .. module:: transaction._manager .. autoclass:: TransactionManager :members: :member-order: bysource .. automethod:: __enter__ Alias for :meth:`get` .. automethod:: __exit__ On error, aborts the current transaction. Otherwise, commits. .. autoclass:: ThreadTransactionManager :members: :member-order: bysource transaction-1.4.3/docs/_build/html/_sources/convenience.txt0000664000175000017500000001122712073316126023767 0ustar tseavertseaverTransaction convenience support =============================== (We *really* need to write proper documentation for the transaction package, but I don't want to block the conveniences documented here for that.) with support ------------ We can now use the with statement to define transaction boundaries. .. doctest:: >>> import transaction.tests.savepointsample >>> dm = transaction.tests.savepointsample.SampleSavepointDataManager() >>> list(dm.keys()) [] We can use it with a manager: .. doctest:: >>> with transaction.manager as t: ... dm['z'] = 3 ... t.note('test 3') >>> dm['z'] 3 >>> dm.last_note 'test 3' >>> with transaction.manager: #doctest ELLIPSIS ... dm['z'] = 4 ... xxx Traceback (most recent call last): ... NameError: ... name 'xxx' is not defined >>> dm['z'] 3 On Python 2, you can also abbreviate ``with transaction.manager:`` as ``with transaction:``. This does not work on Python 3 (see see http://bugs.python.org/issue12022). Retries ------- Commits can fail for transient reasons, especially conflicts. Applications will often retry transactions some number of times to overcome transient failures. This typically looks something like: .. doctest:: for i in range(3): try: with transaction.manager: ... some something ... except SomeTransientException: contine else: break This is rather ugly. Transaction managers provide a helper for this case. To show this, we'll use a contrived example: .. doctest:: >>> ntry = 0 >>> with transaction.manager: ... dm['ntry'] = 0 >>> import transaction.interfaces >>> class Retry(transaction.interfaces.TransientError): ... pass >>> for attempt in transaction.manager.attempts(): ... with attempt as t: ... t.note('test') ... print("%s %s" % (dm['ntry'], ntry)) ... ntry += 1 ... dm['ntry'] = ntry ... if ntry % 3: ... raise Retry(ntry) 0 0 0 1 0 2 The raising of a subclass of TransientError is critical here. It's what signals that the transaction should be retried. It is generally up to the data manager to signal that a transaction should try again by raising a subclass of TransientError (or TransientError itself, of course). You shouldn't make any assumptions about the object returned by the iterator. (It isn't a transaction or transaction manager, as far as you know. :) If you use the ``as`` keyword in the ``with`` statement, a transaction object will be assigned to the variable named. By default, it tries 3 times. We can tell it how many times to try: .. doctest:: >>> for attempt in transaction.manager.attempts(2): ... with attempt: ... ntry += 1 ... if ntry % 3: ... raise Retry(ntry) Traceback (most recent call last): ... Retry: 5 It it doesn't succeed in that many times, the exception will be propagated. Of course, other errors are propagated directly: .. doctest:: >>> ntry = 0 >>> for attempt in transaction.manager.attempts(): ... with attempt: ... ntry += 1 ... if ntry == 3: ... raise ValueError(ntry) Traceback (most recent call last): ... ValueError: 3 We can use the default transaction manager: .. doctest:: >>> for attempt in transaction.attempts(): ... with attempt as t: ... t.note('test') ... print("%s %s" % (dm['ntry'], ntry)) ... ntry += 1 ... dm['ntry'] = ntry ... if ntry % 3: ... raise Retry(ntry) 3 3 3 4 3 5 Sometimes, a data manager doesn't raise exceptions directly, but wraps other other systems that raise exceptions outside of it's control. Data managers can provide a should_retry method that takes an exception instance and returns True if the transaction should be attempted again. .. doctest:: >>> class DM(transaction.tests.savepointsample.SampleSavepointDataManager): ... def should_retry(self, e): ... if 'should retry' in str(e): ... return True >>> ntry = 0 >>> dm2 = DM() >>> with transaction.manager: ... dm2['ntry'] = 0 >>> for attempt in transaction.manager.attempts(): ... with attempt: ... print("%s %s" % (dm['ntry'], ntry)) ... ntry += 1 ... dm['ntry'] = ntry ... dm2['ntry'] = ntry ... if ntry % 3: ... raise ValueError('we really should retry this') 6 0 6 1 6 2 >>> dm2['ntry'] 3 transaction-1.4.3/docs/_build/html/_sources/hooks.txt0000664000175000017500000002257312073316126022624 0ustar tseavertseaverHooking the Transaction Machinery ================================= The :meth:`addBeforeCommitHook` Method -------------------------------------- Let's define a hook to call, and a way to see that it was called. .. doctest:: >>> log = [] >>> def reset_log(): ... del log[:] >>> def hook(arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... log.append("arg %r kw1 %r kw2 %r" % (arg, kw1, kw2)) Now register the hook with a transaction. .. doctest:: >>> from transaction import begin >>> from transaction._compat import func_name >>> import transaction >>> t = begin() >>> t.addBeforeCommitHook(hook, '1') We can see that the hook is indeed registered. .. doctest:: >>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getBeforeCommitHooks()] [('hook', ('1',), {})] When transaction commit starts, the hook is called, with its arguments. .. doctest:: >>> log [] >>> t.commit() >>> log ["arg '1' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log() A hook's registration is consumed whenever the hook is called. Since the hook above was called, it's no longer registered: .. doctest:: >>> from transaction import commit >>> len(list(t.getBeforeCommitHooks())) 0 >>> commit() >>> log [] The hook is only called for a full commit, not for a savepoint. .. doctest:: >>> t = begin() >>> t.addBeforeCommitHook(hook, 'A', dict(kw1='B')) >>> dummy = t.savepoint() >>> log [] >>> t.commit() >>> log ["arg 'A' kw1 'B' kw2 'no_kw2'"] >>> reset_log() If a transaction is aborted, no hook is called. .. doctest:: >>> from transaction import abort >>> t = begin() >>> t.addBeforeCommitHook(hook, ["OOPS!"]) >>> abort() >>> log [] >>> commit() >>> log [] The hook is called before the commit does anything, so even if the commit fails the hook will have been called. To provoke failures in commit, we'll add failing resource manager to the transaction. .. doctest:: >>> class CommitFailure(Exception): ... pass >>> class FailingDataManager: ... def tpc_begin(self, txn, sub=False): ... raise CommitFailure('failed') ... def abort(self, txn): ... pass >>> t = begin() >>> t.join(FailingDataManager()) >>> t.addBeforeCommitHook(hook, '2') >>> from transaction.tests.common import DummyFile >>> from transaction.tests.common import Monkey >>> from transaction.tests.common import assertRaisesEx >>> from transaction import _transaction >>> buffer = DummyFile() >>> with Monkey(_transaction, _TB_BUFFER=buffer): ... err = assertRaisesEx(CommitFailure, t.commit) >>> log ["arg '2' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log() Let's register several hooks. .. doctest:: >>> t = begin() >>> t.addBeforeCommitHook(hook, '4', dict(kw1='4.1')) >>> t.addBeforeCommitHook(hook, '5', dict(kw2='5.2')) They are returned in the same order by getBeforeCommitHooks. .. doctest:: >>> [(func_name(hook), args, kws) #doctest: +NORMALIZE_WHITESPACE ... for hook, args, kws in t.getBeforeCommitHooks()] [('hook', ('4',), {'kw1': '4.1'}), ('hook', ('5',), {'kw2': '5.2'})] And commit also calls them in this order. .. doctest:: >>> t.commit() >>> len(log) 2 >>> log #doctest: +NORMALIZE_WHITESPACE ["arg '4' kw1 '4.1' kw2 'no_kw2'", "arg '5' kw1 'no_kw1' kw2 '5.2'"] >>> reset_log() While executing, a hook can itself add more hooks, and they will all be called before the real commit starts. .. doctest:: >>> def recurse(txn, arg): ... log.append('rec' + str(arg)) ... if arg: ... txn.addBeforeCommitHook(hook, '-') ... txn.addBeforeCommitHook(recurse, (txn, arg-1)) >>> t = begin() >>> t.addBeforeCommitHook(recurse, (t, 3)) >>> commit() >>> log #doctest: +NORMALIZE_WHITESPACE ['rec3', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec2', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec1', "arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec0'] >>> reset_log() The :meth:`addAfterCommitHook` Method -------------------------------------- Let's define a hook to call, and a way to see that it was called. .. doctest:: >>> log = [] >>> def reset_log(): ... del log[:] >>> def hook(status, arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... log.append("%r arg %r kw1 %r kw2 %r" % (status, arg, kw1, kw2)) Now register the hook with a transaction. .. doctest:: >>> from transaction import begin >>> from transaction._compat import func_name >>> t = begin() >>> t.addAfterCommitHook(hook, '1') We can see that the hook is indeed registered. .. doctest:: >>> [(func_name(hook), args, kws) ... for hook, args, kws in t.getAfterCommitHooks()] [('hook', ('1',), {})] When transaction commit is done, the hook is called, with its arguments. .. doctest:: >>> log [] >>> t.commit() >>> log ["True arg '1' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log() A hook's registration is consumed whenever the hook is called. Since the hook above was called, it's no longer registered: .. doctest:: >>> from transaction import commit >>> len(list(t.getAfterCommitHooks())) 0 >>> commit() >>> log [] The hook is only called after a full commit, not for a savepoint. .. doctest:: >>> t = begin() >>> t.addAfterCommitHook(hook, 'A', dict(kw1='B')) >>> dummy = t.savepoint() >>> log [] >>> t.commit() >>> log ["True arg 'A' kw1 'B' kw2 'no_kw2'"] >>> reset_log() If a transaction is aborted, no hook is called. .. doctest:: >>> from transaction import abort >>> t = begin() >>> t.addAfterCommitHook(hook, ["OOPS!"]) >>> abort() >>> log [] >>> commit() >>> log [] The hook is called after the commit is done, so even if the commit fails the hook will have been called. To provoke failures in commit, we'll add failing resource manager to the transaction. .. doctest:: >>> class CommitFailure(Exception): ... pass >>> class FailingDataManager: ... def tpc_begin(self, txn): ... raise CommitFailure('failed') ... def abort(self, txn): ... pass >>> t = begin() >>> t.join(FailingDataManager()) >>> t.addAfterCommitHook(hook, '2') >>> from transaction.tests.common import DummyFile >>> from transaction.tests.common import Monkey >>> from transaction.tests.common import assertRaisesEx >>> from transaction import _transaction >>> buffer = DummyFile() >>> with Monkey(_transaction, _TB_BUFFER=buffer): ... err = assertRaisesEx(CommitFailure, t.commit) >>> log ["False arg '2' kw1 'no_kw1' kw2 'no_kw2'"] >>> reset_log() Let's register several hooks. .. doctest:: >>> t = begin() >>> t.addAfterCommitHook(hook, '4', dict(kw1='4.1')) >>> t.addAfterCommitHook(hook, '5', dict(kw2='5.2')) They are returned in the same order by getAfterCommitHooks. .. doctest:: >>> [(func_name(hook), args, kws) #doctest: +NORMALIZE_WHITESPACE ... for hook, args, kws in t.getAfterCommitHooks()] [('hook', ('4',), {'kw1': '4.1'}), ('hook', ('5',), {'kw2': '5.2'})] And commit also calls them in this order. .. doctest:: >>> t.commit() >>> len(log) 2 >>> log #doctest: +NORMALIZE_WHITESPACE ["True arg '4' kw1 '4.1' kw2 'no_kw2'", "True arg '5' kw1 'no_kw1' kw2 '5.2'"] >>> reset_log() While executing, a hook can itself add more hooks, and they will all be called before the real commit starts. .. doctest:: >>> def recurse(status, txn, arg): ... log.append('rec' + str(arg)) ... if arg: ... txn.addAfterCommitHook(hook, '-') ... txn.addAfterCommitHook(recurse, (txn, arg-1)) >>> t = begin() >>> t.addAfterCommitHook(recurse, (t, 3)) >>> commit() >>> log #doctest: +NORMALIZE_WHITESPACE ['rec3', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec2', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec1', "True arg '-' kw1 'no_kw1' kw2 'no_kw2'", 'rec0'] >>> reset_log() If an after commit hook is raising an exception then it will log a message at error level so that if other hooks are registered they can be executed. We don't support execution dependencies at this level. .. doctest:: >>> from transaction import TransactionManager >>> from transaction.tests.test__manager import DataObject >>> mgr = TransactionManager() >>> do = DataObject(mgr) >>> def hookRaise(status, arg='no_arg', kw1='no_kw1', kw2='no_kw2'): ... raise TypeError("Fake raise") >>> t = begin() >>> t.addAfterCommitHook(hook, ('-', 1)) >>> t.addAfterCommitHook(hookRaise, ('-', 2)) >>> t.addAfterCommitHook(hook, ('-', 3)) >>> commit() >>> log ["True arg '-' kw1 1 kw2 'no_kw2'", "True arg '-' kw1 3 kw2 'no_kw2'"] >>> reset_log() Test that the associated transaction manager has been cleanup when after commit hooks are registered .. doctest:: >>> mgr = TransactionManager() >>> do = DataObject(mgr) >>> t = begin() >>> t._manager._txn is not None True >>> t.addAfterCommitHook(hook, ('-', 1)) >>> commit() >>> log ["True arg '-' kw1 1 kw2 'no_kw2'"] >>> t._manager._txn is not None False >>> reset_log() transaction-1.4.3/docs/_build/html/_sources/savepoint.txt0000664000175000017500000002071712073316126023507 0ustar tseavertseaverSavepoints ========== Savepoints provide a way to save to disk intermediate work done during a transaction allowing: - partial transaction (subtransaction) rollback (abort) - state of saved objects to be freed, freeing on-line memory for other uses Savepoints make it possible to write atomic subroutines that don't make top-level transaction commitments. Applications ------------ To demonstrate how savepoints work with transactions, we've provided a sample data manager implementation that provides savepoint support. The primary purpose of this data manager is to provide code that can be read to understand how savepoints work. The secondary purpose is to provide support for demonstrating the correct operation of savepoint support within the transaction system. This data manager is very simple. It provides flat storage of named immutable values, like strings and numbers. .. doctest:: >>> import transaction >>> from transaction.tests import savepointsample >>> dm = savepointsample.SampleSavepointDataManager() >>> dm['name'] = 'bob' As with other data managers, we can commit changes: .. doctest:: >>> transaction.commit() >>> dm['name'] 'bob' and abort changes: .. doctest:: >>> dm['name'] = 'sally' >>> dm['name'] 'sally' >>> transaction.abort() >>> dm['name'] 'bob' Now, let's look at an application that manages funds for people. It allows deposits and debits to be entered for multiple people. It accepts a sequence of entries and generates a sequence of status messages. For each entry, it applies the change and then validates the user's account. If the user's account is invalid, we roll back the change for that entry. The success or failure of an entry is indicated in the output status. First we'll initialize some accounts: .. doctest:: >>> dm['bob-balance'] = 0.0 >>> dm['bob-credit'] = 0.0 >>> dm['sally-balance'] = 0.0 >>> dm['sally-credit'] = 100.0 >>> transaction.commit() Now, we'll define a validation function to validate an account: .. doctest:: >>> def validate_account(name): ... if dm[name+'-balance'] + dm[name+'-credit'] < 0: ... raise ValueError('Overdrawn', name) And a function to apply entries. If the function fails in some unexpected way, it rolls back all of its changes and prints the error: .. doctest:: >>> def apply_entries(entries): ... savepoint = transaction.savepoint() ... try: ... for name, amount in entries: ... entry_savepoint = transaction.savepoint() ... try: ... dm[name+'-balance'] += amount ... validate_account(name) ... except ValueError as error: ... entry_savepoint.rollback() ... print("%s %s" % ('Error', str(error))) ... else: ... print("%s %s" % ('Updated', name)) ... except Exception as error: ... savepoint.rollback() ... print("%s" % ('Unexpected exception')) Now let's try applying some entries: .. doctest:: >>> apply_entries([ ... ('bob', 10.0), ... ('sally', 10.0), ... ('bob', 20.0), ... ('sally', 10.0), ... ('bob', -100.0), ... ('sally', -100.0), ... ]) Updated bob Updated sally Updated bob Updated sally Error ('Overdrawn', 'bob') Updated sally >>> dm['bob-balance'] 30.0 >>> dm['sally-balance'] -80.0 If we provide entries that cause an unexpected error: .. doctest:: >>> apply_entries([ ... ('bob', 10.0), ... ('sally', 10.0), ... ('bob', '20.0'), ... ('sally', 10.0), ... ]) Updated bob Updated sally Unexpected exception Because the apply_entries used a savepoint for the entire function, it was able to rollback the partial changes without rolling back changes made in the previous call to ``apply_entries``: .. doctest:: >>> dm['bob-balance'] 30.0 >>> dm['sally-balance'] -80.0 If we now abort the outer transactions, the earlier changes will go away: .. doctest:: >>> transaction.abort() >>> dm['bob-balance'] 0.0 >>> dm['sally-balance'] 0.0 Savepoint invalidation ---------------------- A savepoint can be used any number of times: .. doctest:: >>> dm['bob-balance'] = 100.0 >>> dm['bob-balance'] 100.0 >>> savepoint = transaction.savepoint() >>> dm['bob-balance'] = 200.0 >>> dm['bob-balance'] 200.0 >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 >>> savepoint.rollback() # redundant, but should be harmless >>> dm['bob-balance'] 100.0 >>> dm['bob-balance'] = 300.0 >>> dm['bob-balance'] 300.0 >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 However, using a savepoint invalidates any savepoints that come after it: .. doctest:: >>> dm['bob-balance'] = 200.0 >>> dm['bob-balance'] 200.0 >>> savepoint1 = transaction.savepoint() >>> dm['bob-balance'] = 300.0 >>> dm['bob-balance'] 300.0 >>> savepoint2 = transaction.savepoint() >>> savepoint.rollback() >>> dm['bob-balance'] 100.0 >>> savepoint2.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... InvalidSavepointRollbackError: invalidated by a later savepoint >>> savepoint1.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... InvalidSavepointRollbackError: invalidated by a later savepoint >>> transaction.abort() Databases without savepoint support ----------------------------------- Normally it's an error to use savepoints with databases that don't support savepoints: .. doctest:: >>> dm_no_sp = savepointsample.SampleDataManager() >>> dm_no_sp['name'] = 'bob' >>> transaction.commit() >>> dm_no_sp['name'] = 'sally' >>> transaction.savepoint() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'bob'}) >>> transaction.abort() However, a flag can be passed to the transaction savepoint method to indicate that databases without savepoint support should be tolerated until a savepoint is rolled back. This allows transactions to proceed if there are no reasons to roll back: .. doctest:: >>> dm_no_sp['name'] = 'sally' >>> savepoint = transaction.savepoint(1) >>> dm_no_sp['name'] = 'sue' >>> transaction.commit() >>> dm_no_sp['name'] 'sue' >>> dm_no_sp['name'] = 'sam' >>> savepoint = transaction.savepoint(1) >>> savepoint.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'sam'}) Failures -------- If a failure occurs when creating or rolling back a savepoint, the transaction state will be uncertain and the transaction will become uncommitable. From that point on, most transaction operations, including commit, will fail until the transaction is aborted. In the previous example, we got an error when we tried to rollback the savepoint. If we try to commit the transaction, the commit will fail: .. doctest:: >>> transaction.commit() #doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TransactionFailedError: An operation previously failed, with traceback: ... TypeError: ('Savepoints unsupported', {'name': 'sam'}) We have to abort it to make any progress: .. doctest:: >>> transaction.abort() Similarly, in our earlier example, where we tried to take a savepoint with a data manager that didn't support savepoints: .. doctest:: >>> dm_no_sp['name'] = 'sally' >>> dm['name'] = 'sally' >>> savepoint = transaction.savepoint() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: ('Savepoints unsupported', {'name': 'sue'}) >>> transaction.commit() # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TransactionFailedError: An operation previously failed, with traceback: ... TypeError: ('Savepoints unsupported', {'name': 'sue'}) >>> transaction.abort() After clearing the transaction with an abort, we can get on with new transactions: .. doctest:: >>> dm_no_sp['name'] = 'sally' >>> dm['name'] = 'sally' >>> transaction.commit() >>> dm_no_sp['name'] 'sally' >>> dm['name'] 'sally' transaction-1.4.3/docs/_build/html/_sources/resourcemanager.txt0000664000175000017500000001757012073316126024664 0ustar tseavertseaverWriting a Resource Manager ========================== Simple Resource Manager ----------------------- .. doctest:: >>> from transaction.tests.examples import ResourceManager This :class:`transaction.tests.examples.ResourceManager` class provides a trivial resource-manager implementation and doc strings to illustrate the protocol and to provide a tool for writing tests. Our sample resource manager has state that is updated through an inc method and through transaction operations. When we create a sample resource manager: .. doctest:: >>> rm = ResourceManager() It has two pieces state, state and delta, both initialized to 0: .. doctest:: >>> rm.state 0 >>> rm.delta 0 state is meant to model committed state, while delta represents tentative changes within a transaction. We change the state by calling inc: .. doctest:: >>> rm.inc() which updates delta: .. doctest:: >>> rm.delta 1 but state isn't changed until we commit the transaction: .. doctest:: >>> rm.state 0 To commit the changes, we use 2-phase commit. We execute the first stage by calling prepare. We need to pass a transation. Our sample resource managers don't really use the transactions for much, so we'll be lazy and use strings for transactions. The sample resource manager updates the state when we call tpc_vote: .. doctest:: >>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.state, rm.delta (0, 1) >>> rm.tpc_vote(t1) >>> rm.state, rm.delta (1, 1) Now if we call tpc_finish: >>> rm.tpc_finish(t1) Our changes are "permanent". The state reflects the changes and the delta has been reset to 0. .. doctest:: >>> rm.state, rm.delta (1, 0) The :meth:`tpc_begin` Method ----------------------------- Called by the transaction manager to ask the RM to prepare to commit data. .. doctest:: >>> rm = ResourceManager() >>> rm.inc() >>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> rm.state 1 >>> rm.inc() >>> t2 = '2' >>> rm.tpc_begin(t2) >>> rm.tpc_vote(t2) >>> rm.tpc_abort(t2) >>> rm.state 1 It is an error to call tpc_begin more than once without completing two-phase commit: .. doctest:: >>> rm.tpc_begin(t1) >>> rm.tpc_begin(t1) Traceback (most recent call last): ... ValueError: txn in state 'tpc_begin' but expected one of (None,) >>> rm.tpc_abort(t1) If there was a preceeding savepoint, the transaction must match: .. doctest:: >>> rollback = rm.savepoint(t1) >>> rm.tpc_begin(t2) Traceback (most recent call last): ,,, TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_begin(t1) The :meth:`tpc_vote` Method --------------------------- Verify that a data manager can commit the transaction. This is the last chance for a data manager to vote 'no'. A data manager votes 'no' by raising an exception. Passed `transaction`, which is the ITransaction instance associated with the transaction being committed. The :meth:`tpc_finish` Method ----------------------------- Complete two-phase commit .. doctest:: >>> rm = ResourceManager() >>> rm.state 0 >>> rm.inc() We start two-phase commit by calling prepare: >>> t1 = '1' >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) We complete it by calling tpc_finish: >>> rm.tpc_finish(t1) >>> rm.state 1 It is an error ro call tpc_finish without calling tpc_vote: .. doctest:: >>> rm.inc() >>> t2 = '2' >>> rm.tpc_begin(t2) >>> rm.tpc_finish(t2) Traceback (most recent call last): ... ValueError: txn in state 'tpc_begin' but expected one of ('tpc_vote',) >>> rm.tpc_abort(t2) # clean slate >>> rm.tpc_begin(t2) >>> rm.tpc_vote(t2) >>> rm.tpc_finish(t2) Of course, the transactions given to tpc_begin and tpc_finish must be the same: .. doctest:: >>> rm.inc() >>> t3 = '3' >>> rm.tpc_begin(t3) >>> rm.tpc_vote(t3) >>> rm.tpc_finish(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '3') The :meth:`tpc_abort` Method ----------------------------- Abort a transaction The abort method can be called before two-phase commit to throw away work done in the transaction: .. doctest:: >>> rm = ResourceManager() >>> rm.inc() >>> rm.state, rm.delta (0, 1) >>> t1 = '1' >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0) The abort method also throws away work done in savepoints: .. doctest:: >>> rm.inc() >>> r = rm.savepoint(t1) >>> rm.inc() >>> r = rm.savepoint(t1) >>> rm.state, rm.delta (0, 2) >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0) If savepoints are used, abort must be passed the same transaction: .. doctest:: >>> rm.inc() >>> r = rm.savepoint(t1) >>> t2 = '2' >>> rm.tpc_abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_abort(t1) The abort method is also used to abort a two-phase commit: .. doctest:: >>> rm.inc() >>> rm.state, rm.delta (0, 1) >>> rm.tpc_begin(t1) >>> rm.state, rm.delta (0, 1) >>> rm.tpc_vote(t1) >>> rm.state, rm.delta (1, 1) >>> rm.tpc_abort(t1) >>> rm.state, rm.delta (0, 0) Of course, the transactions passed to prepare and abort must match: .. doctest:: >>> rm.tpc_begin(t1) >>> rm.tpc_abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> rm.tpc_abort(t1) This should never fail. The :meth:`savepoint` Method ---------------------------- Provide the ability to rollback transaction state Savepoints provide a way to: - Save partial transaction work. For some resource managers, this could allow resources to be used more efficiently. - Provide the ability to revert state to a point in a transaction without aborting the entire transaction. In other words, savepoints support partial aborts. Savepoints don't use two-phase commit. If there are errors in setting or rolling back to savepoints, the application should abort the containing transaction. This is *not* the responsibility of the resource manager. Savepoints are always associated with a transaction. Any work done in a savepoint's transaction is tentative until the transaction is committed using two-phase commit. .. doctest:: >>> rm = ResourceManager() >>> rm.inc() >>> t1 = '1' >>> r = rm.savepoint(t1) >>> rm.state, rm.delta (0, 1) >>> rm.inc() >>> rm.state, rm.delta (0, 2) >>> r.rollback() >>> rm.state, rm.delta (0, 1) >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> rm.state, rm.delta (1, 0) Savepoints must have the same transaction: .. doctest:: >>> r1 = rm.savepoint(t1) >>> rm.state, rm.delta (1, 0) >>> rm.inc() >>> rm.state, rm.delta (1, 1) >>> t2 = '2' >>> r2 = rm.savepoint(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> r2 = rm.savepoint(t1) >>> rm.inc() >>> rm.state, rm.delta (1, 2) If we rollback to an earlier savepoint, we discard all work done later: .. doctest:: >>> r1.rollback() >>> rm.state, rm.delta (1, 0) and we can no longer rollback to the later savepoint: .. doctest:: >>> r2.rollback() Traceback (most recent call last): ... TypeError: ('Attempt to roll back to invalid save point', 3, 2) We can roll back to a savepoint as often as we like: .. doctest:: >>> r1.rollback() >>> r1.rollback() >>> r1.rollback() >>> rm.state, rm.delta (1, 0) >>> rm.inc() >>> rm.inc() >>> rm.inc() >>> rm.state, rm.delta (1, 3) >>> r1.rollback() >>> rm.state, rm.delta (1, 0) But we can't rollback to a savepoint after it has been committed: .. doctest:: >>> rm.tpc_begin(t1) >>> rm.tpc_vote(t1) >>> rm.tpc_finish(t1) >>> r1.rollback() Traceback (most recent call last): ... TypeError: Attempt to rollback stale rollback transaction-1.4.3/docs/_build/html/_sources/datamanager.txt0000664000175000017500000001616612073316126023746 0ustar tseavertseaverWriting a Data Manager ====================== Simple Data Manager ------------------ .. doctest:: >>> from transaction.tests.examples import DataManager This :class:`transaction.tests.examples.DataManager` class provides a trivial data-manager implementation and docstrings to illustrate the the protocol and to provide a tool for writing tests. Our sample data manager has state that is updated through an inc method and through transaction operations. When we create a sample data manager: .. doctest:: >>> dm = DataManager() It has two bits of state, state: .. doctest:: >>> dm.state 0 and delta: .. doctest:: >>> dm.delta 0 Both of which are initialized to 0. state is meant to model committed state, while delta represents tentative changes within a transaction. We change the state by calling inc: .. doctest:: >>> dm.inc() which updates delta: .. doctest:: >>> dm.delta 1 but state isn't changed until we commit the transaction: .. doctest:: >>> dm.state 0 To commit the changes, we use 2-phase commit. We execute the first stage by calling prepare. We need to pass a transation. Our sample data managers don't really use the transactions for much, so we'll be lazy and use strings for transactions: .. doctest:: >>> t1 = '1' >>> dm.prepare(t1) The sample data manager updates the state when we call prepare: .. doctest:: >>> dm.state 1 >>> dm.delta 1 This is mainly so we can detect some affect of calling the methods. Now if we call commit: .. doctest:: >>> dm.commit(t1) Our changes are"permanent". The state reflects the changes and the delta has been reset to 0. .. doctest:: >>> dm.state 1 >>> dm.delta 0 The :meth:`prepare` Method ---------------------------- Prepare to commit data .. doctest:: >>> dm = DataManager() >>> dm.inc() >>> t1 = '1' >>> dm.prepare(t1) >>> dm.commit(t1) >>> dm.state 1 >>> dm.inc() >>> t2 = '2' >>> dm.prepare(t2) >>> dm.abort(t2) >>> dm.state 1 It is en error to call prepare more than once without an intervening commit or abort: .. doctest:: >>> dm.prepare(t1) >>> dm.prepare(t1) Traceback (most recent call last): ... TypeError: Already prepared >>> dm.prepare(t2) Traceback (most recent call last): ... TypeError: Already prepared >>> dm.abort(t1) If there was a preceeding savepoint, the transaction must match: .. doctest:: >>> rollback = dm.savepoint(t1) >>> dm.prepare(t2) Traceback (most recent call last): ,,, TypeError: ('Transaction missmatch', '2', '1') >>> dm.prepare(t1) The :meth:`abort` method -------------------------- The abort method can be called before two-phase commit to throw away work done in the transaction: .. doctest:: >>> dm = DataManager() >>> dm.inc() >>> dm.state, dm.delta (0, 1) >>> t1 = '1' >>> dm.abort(t1) >>> dm.state, dm.delta (0, 0) The abort method also throws away work done in savepoints: .. doctest:: >>> dm.inc() >>> r = dm.savepoint(t1) >>> dm.inc() >>> r = dm.savepoint(t1) >>> dm.state, dm.delta (0, 2) >>> dm.abort(t1) >>> dm.state, dm.delta (0, 0) If savepoints are used, abort must be passed the same transaction: .. doctest:: >>> dm.inc() >>> r = dm.savepoint(t1) >>> t2 = '2' >>> dm.abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> dm.abort(t1) The abort method is also used to abort a two-phase commit: .. doctest:: >>> dm.inc() >>> dm.state, dm.delta (0, 1) >>> dm.prepare(t1) >>> dm.state, dm.delta (1, 1) >>> dm.abort(t1) >>> dm.state, dm.delta (0, 0) Of course, the transactions passed to prepare and abort must match: .. doctest:: >>> dm.prepare(t1) >>> dm.abort(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> dm.abort(t1) The :meth:`commit` method --------------------------- Called to omplete two-phase commit .. doctest:: >>> dm = DataManager() >>> dm.state 0 >>> dm.inc() We start two-phase commit by calling prepare: .. doctest:: >>> t1 = '1' >>> dm.prepare(t1) We complete it by calling commit: .. doctest:: >>> dm.commit(t1) >>> dm.state 1 It is an error ro call commit without calling prepare first: .. doctest:: >>> dm.inc() >>> t2 = '2' >>> dm.commit(t2) Traceback (most recent call last): ... TypeError: Not prepared to commit >>> dm.prepare(t2) >>> dm.commit(t2) If course, the transactions given to prepare and commit must be the same: .. doctest:: >>> dm.inc() >>> t3 = '3' >>> dm.prepare(t3) >>> dm.commit(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '3') The :meth:`savepoint` method ------------------------------ Provide the ability to rollback transaction state Savepoints provide a way to: - Save partial transaction work. For some data managers, this could allow resources to be used more efficiently. - Provide the ability to revert state to a point in a transaction without aborting the entire transaction. In other words, savepoints support partial aborts. Savepoints don't use two-phase commit. If there are errors in setting or rolling back to savepoints, the application should abort the containing transaction. This is *not* the responsibility of the data manager. Savepoints are always associated with a transaction. Any work done in a savepoint's transaction is tentative until the transaction is committed using two-phase commit. .. doctest:: >>> dm = DataManager() >>> dm.inc() >>> t1 = '1' >>> r = dm.savepoint(t1) >>> dm.state, dm.delta (0, 1) >>> dm.inc() >>> dm.state, dm.delta (0, 2) >>> r.rollback() >>> dm.state, dm.delta (0, 1) >>> dm.prepare(t1) >>> dm.commit(t1) >>> dm.state, dm.delta (1, 0) Savepoints must have the same transaction: .. doctest:: >>> r1 = dm.savepoint(t1) >>> dm.state, dm.delta (1, 0) >>> dm.inc() >>> dm.state, dm.delta (1, 1) >>> t2 = '2' >>> r2 = dm.savepoint(t2) Traceback (most recent call last): ... TypeError: ('Transaction missmatch', '2', '1') >>> r2 = dm.savepoint(t1) >>> dm.inc() >>> dm.state, dm.delta (1, 2) If we rollback to an earlier savepoint, we discard all work done later: .. doctest:: >>> r1.rollback() >>> dm.state, dm.delta (1, 0) and we can no longer rollback to the later savepoint: .. doctest:: >>> r2.rollback() Traceback (most recent call last): ... TypeError: ('Attempt to roll back to invalid save point', 3, 2) We can roll back to a savepoint as often as we like: .. doctest:: >>> r1.rollback() >>> r1.rollback() >>> r1.rollback() >>> dm.state, dm.delta (1, 0) >>> dm.inc() >>> dm.inc() >>> dm.inc() >>> dm.state, dm.delta (1, 3) >>> r1.rollback() >>> dm.state, dm.delta (1, 0) But we can't rollback to a savepoint after it has been committed: .. doctest:: >>> dm.prepare(t1) >>> dm.commit(t1) >>> r1.rollback() Traceback (most recent call last): ... TypeError: Attempt to rollback stale rollback transaction-1.4.3/docs/_build/html/_sources/index.txt0000664000175000017500000000742112073316126022603 0ustar tseavertseaver:mod:`transaction` Documentation ================================ Transaction objects manage resources for an individual activity. Compatibility issues -------------------- The implementation of Transaction objects involves two layers of backwards compatibility, because this version of transaction supports both ZODB 3 and ZODB 4. Zope is evolving towards the ZODB4 interfaces. Transaction has two methods for a resource manager to call to participate in a transaction -- register() and join(). join() takes a resource manager and adds it to the list of resources. register() is for backwards compatibility. It takes a persistent object and registers its _p_jar attribute. TODO: explain adapter Two-phase commit ---------------- A transaction commit involves an interaction between the transaction object and one or more resource managers. The transaction manager calls the following four methods on each resource manager; it calls tpc_begin() on each resource manager before calling commit() on any of them. 1. tpc_begin(txn) 2. commit(txn) 3. tpc_vote(txn) 4. tpc_finish(txn) Before-commit hook ------------------ Sometimes, applications want to execute some code when a transaction is committed. For example, one might want to delay object indexing until a transaction commits, rather than indexing every time an object is changed. Or someone might want to check invariants only after a set of operations. A pre-commit hook is available for such use cases: use addBeforeCommitHook(), passing it a callable and arguments. The callable will be called with its arguments at the start of the commit (but not for substransaction commits). After-commit hook ------------------ Sometimes, applications want to execute code after a transaction commit attempt succeeds or aborts. For example, one might want to launch non transactional code after a successful commit. Or still someone might want to launch asynchronous code after. A post-commit hook is available for such use cases: use addAfterCommitHook(), passing it a callable and arguments. The callable will be called with a Boolean value representing the status of the commit operation as first argument (true if successfull or false iff aborted) preceding its arguments at the start of the commit (but not for substransaction commits). Commit hooks are not called for transaction.abort(). Error handling -------------- When errors occur during two-phase commit, the transaction manager aborts all the resource managers. The specific methods it calls depend on whether the error occurs before or after the call to tpc_vote() on that transaction manager. If the resource manager has not voted, then the resource manager will have one or more uncommitted objects. There are two cases that lead to this state; either the transaction manager has not called commit() for any objects on this resource manager or the call that failed was a commit() for one of the objects of this resource manager. For each uncommitted object, including the object that failed in its commit(), call abort(). Once uncommitted objects are aborted, tpc_abort() or abort_sub() is called on each resource manager. Synchronization --------------- You can register sychronization objects (synchronizers) with the tranasction manager. The synchronizer must implement beforeCompletion() and afterCompletion() methods. The transaction manager calls beforeCompletion() when it starts a top-level two-phase commit. It calls afterCompletion() when a top-level transaction is committed or aborted. The methods are passed the current Transaction as their only argument. Contents: .. toctree:: :maxdepth: 2 convenience doom savepoint hooks datamanager resourcemanager api Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` transaction-1.4.3/docs/_build/html/api.html0000664000175000017500000015570212312641212020547 0ustar tseavertseaver transaction API Reference — transaction 1.2 documentation

transaction API Reference

Interfaces

interface transaction.interfaces.ITransactionManager[source]

An object that manages a sequence of transactions.

Applications use transaction managers to establish transaction boundaries.

begin()[source]

Begin a new transaction.

If an existing transaction is in progress, it will be aborted.

The newTransaction() method of registered synchronizers is called, passing the new transaction object.

get()[source]

Get the current transaction.

commit()[source]

Commit the current transaction.

abort()[source]

Abort the current transaction.

doom()[source]

Doom the current transaction.

isDoomed()[source]

Returns True if the current transaction is doomed, otherwise False.

savepoint(optimistic=False)[source]

Create a savepoint from the current transaction.

If the optimistic argument is true, then data managers that don’t support savepoints can be used, but an error will be raised if the savepoint is rolled back.

An ISavepoint object is returned.

registerSynch(synch)[source]

Register an ISynchronizer.

Synchronizers are notified about some major events in a transaction’s life. See ISynchronizer for details.

unregisterSynch(synch)[source]

Unregister an ISynchronizer.

Synchronizers are notified about some major events in a transaction’s life. See ISynchronizer for details.

interface transaction.interfaces.ITransaction[source]

Object representing a running transaction.

Objects with this interface may represent different transactions during their lifetime (.begin() can be called to start a new transaction using the same instance, although that example is deprecated and will go away in ZODB 3.6).

user

A user name associated with the transaction.

The format of the user name is defined by the application. The value is of Python type str. Storages record the user value, as meta-data, when a transaction commits.

A storage may impose a limit on the size of the value; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or truncate the value).

description

A textual description of the transaction.

The value is of Python type str. Method note() is the intended way to set the value. Storages record the description, as meta-data, when a transaction commits.

A storage may impose a limit on the size of the description; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or truncate the value).

commit()[source]

Finalize the transaction.

This executes the two-phase commit algorithm for all IDataManager objects associated with the transaction.

abort()[source]

Abort the transaction.

This is called from the application. This can only be called before the two-phase commit protocol has been started.

doom()[source]

Doom the transaction.

Dooms the current transaction. This will cause DoomedTransactionException to be raised on any attempt to commit the transaction.

Otherwise the transaction will behave as if it was active.

savepoint(optimistic=False)[source]

Create a savepoint.

If the optimistic argument is true, then data managers that don’t support savepoints can be used, but an error will be raised if the savepoint is rolled back.

An ISavepoint object is returned.

join(datamanager)[source]

Add a data manager to the transaction.

datamanager must provide the transactions.interfaces.IDataManager interface.

note(text)[source]

Add text to the transaction description.

This modifies the .description attribute; see its docs for more detail. First surrounding whitespace is stripped from text. If .description is currently an empty string, then the stripped text becomes its value, else two newlines and the stripped text are appended to .description.

setUser(user_name, path='/')[source]

Set the user name.

path should be provided if needed to further qualify the identified user. This is a convenience method used by Zope. It sets the .user attribute to str(path) + ” ” + str(user_name). This sets the .user attribute; see its docs for more detail.

setExtendedInfo(name, value)[source]

Add extension data to the transaction.

name is the name of the extension property to set, of Python type str; value must be picklable. Multiple calls may be made to set multiple extension properties, provided the names are distinct.

Storages record the extension data, as meta-data, when a transaction commits.

A storage may impose a limit on the size of extension data; behavior is undefined if such a limit is exceeded (for example, a storage may raise an exception, or remove <name, value> pairs).

beforeCommitHook(_ITransaction__hook, *args, **kws)[source]

Register a hook to call before the transaction is committed.

THIS IS DEPRECATED IN ZODB 3.6. Use addBeforeCommitHook() instead.

The specified hook function will be called after the transaction’s commit method has been called, but before the commit process has been started. The hook will be passed the specified positional and keyword arguments.

Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks.

Hooks are called only for a top-level commit. A savepoint does not call any hooks. If the transaction is aborted, hooks are not called, and are discarded. Calling a hook “consumes” its registration too: hook registrations do not persist across transactions. If it’s desired to call the same hook on every transaction commit, then beforeCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager’s registerSynch() method instead.

addBeforeCommitHook(hook, args=(), kws=None)[source]

Register a hook to call before the transaction is committed.

The specified hook function will be called after the transaction’s commit method has been called, but before the commit process has been started. The hook will be passed the specified positional (args) and keyword (kws) arguments. args is a sequence of positional arguments to be passed, defaulting to an empty tuple (no positional arguments are passed). kws is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed).

Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks.

Hooks are called only for a top-level commit. A savepoint creation does not call any hooks. If the transaction is aborted, hooks are not called, and are discarded. Calling a hook “consumes” its registration too: hook registrations do not persist across transactions. If it’s desired to call the same hook on every transaction commit, then addBeforeCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager’s registerSynch() method instead.

getBeforeCommitHooks()[source]

Return iterable producing the registered addBeforeCommit hooks.

A triple (hook, args, kws) is produced for each registered hook. The hooks are produced in the order in which they would be invoked by a top-level transaction commit.

addAfterCommitHook(hook, args=(), kws=None)[source]

Register a hook to call after a transaction commit attempt.

The specified hook function will be called after the transaction commit succeeds or aborts. The first argument passed to the hook is a Boolean value, true if the commit succeeded, or false if the commit aborted. args specifies additional positional, and kws keyword, arguments to pass to the hook. args is a sequence of positional arguments to be passed, defaulting to an empty tuple (only the true/false success argument is passed). kws is a dictionary of keyword argument names and values to be passed, or the default None (no keyword arguments are passed).

Multiple hooks can be registered and will be called in the order they were registered (first registered, first called). This method can also be called from a hook: an executing hook can register more hooks. Applications should take care to avoid creating infinite loops by recursively registering hooks.

Hooks are called only for a top-level commit. A savepoint creation does not call any hooks. Calling a hook “consumes” its registration: hook registrations do not persist across transactions. If it’s desired to call the same hook on every transaction commit, then addAfterCommitHook() must be called with that hook during every transaction; in such a case consider registering a synchronizer object via a TransactionManager’s registerSynch() method instead.

getAfterCommitHooks()[source]

Return iterable producing the registered addAfterCommit hooks.

A triple (hook, args, kws) is produced for each registered hook. The hooks are produced in the order in which they would be invoked by a top-level transaction commit.

interface transaction.interfaces.IDataManager[source]

Objects that manage transactional storage.

These objects may manage data for other objects, or they may manage non-object storages, such as relational databases. For example, a ZODB.Connection.

Note that when some data is modified, that data’s data manager should join a transaction so that data can be committed when the user commits the transaction.

transaction_manager

The transaction manager (TM) used by this data manager.

This is a public attribute, intended for read-only use. The value is an instance of ITransactionManager, typically set by the data manager’s constructor.

abort(transaction)[source]

Abort a transaction and forget all changes.

Abort must be called outside of a two-phase commit.

Abort is called by the transaction manager to abort transactions that are not yet in a two-phase commit. It may also be called when rolling back a savepoint made before the data manager joined the transaction.

In any case, after abort is called, the data manager is no longer participating in the transaction. If there are new changes, the data manager must rejoin the transaction.

tpc_begin(transaction)[source]

Begin commit of a transaction, starting the two-phase commit.

transaction is the ITransaction instance associated with the transaction being committed.

commit(transaction)[source]

Commit modifications to registered objects.

Save changes to be made persistent if the transaction commits (if tpc_finish is called later). If tpc_abort is called later, changes must not persist.

This includes conflict detection and handling. If no conflicts or errors occur, the data manager should be prepared to make the changes persist when tpc_finish is called.

tpc_vote(transaction)[source]

Verify that a data manager can commit the transaction.

This is the last chance for a data manager to vote ‘no’. A data manager votes ‘no’ by raising an exception.

transaction is the ITransaction instance associated with the transaction being committed.

tpc_finish(transaction)[source]

Indicate confirmation that the transaction is done.

Make all changes to objects modified by this transaction persist.

transaction is the ITransaction instance associated with the transaction being committed.

This should never fail. If this raises an exception, the database is not expected to maintain consistency; it’s a serious error.

tpc_abort(transaction)[source]

Abort a transaction.

This is called by a transaction manager to end a two-phase commit on the data manager. Abandon all changes to objects modified by this transaction.

transaction is the ITransaction instance associated with the transaction being committed.

This should never fail.

sortKey()[source]

Return a key to use for ordering registered DataManagers.

In order to guarantee a total ordering, keys must be strings.

ZODB uses a global sort order to prevent deadlock when it commits transactions involving multiple resource managers. The resource manager must define a sortKey() method that provides a global ordering for resource managers.

interface transaction.interfaces.ISavepointDataManager[source]

Extends: transaction.interfaces.IDataManager

savepoint()[source]

Return a data-manager savepoint (IDataManagerSavepoint).

interface transaction.interfaces.IDataManagerSavepoint[source]

Savepoint for data-manager changes for use in transaction savepoints.

Datamanager savepoints are used by, and only by, transaction savepoints.

Note that data manager savepoints don’t have any notion of, or responsibility for, validity. It isn’t the responsibility of data-manager savepoints to prevent multiple rollbacks or rollbacks after transaction termination. Preventing invalid savepoint rollback is the responsibility of transaction rollbacks. Application code should never use data-manager savepoints.

rollback()[source]

Rollback any work done since the savepoint.

interface transaction.interfaces.ISavepoint[source]

A transaction savepoint.

rollback()[source]

Rollback any work done since the savepoint.

InvalidSavepointRollbackError is raised if the savepoint isn’t valid.

valid

Boolean indicating whether the savepoint is valid

class transaction.interfaces.InvalidSavepointRollbackError[source]

Attempt to rollback an invalid savepoint.

A savepoint may be invalid because:

  • The surrounding transaction has committed or aborted.
  • An earlier savepoint in the same transaction has been rolled back.
interface transaction.interfaces.ISynchronizer[source]

Objects that participate in the transaction-boundary notification API.

beforeCompletion(transaction)[source]

Hook that is called by the transaction at the start of a commit.

afterCompletion(transaction)[source]

Hook that is called by the transaction after completing a commit.

newTransaction(transaction)[source]

Hook that is called at the start of a transaction.

This hook is called when, and only when, a transaction manager’s begin() method is called explictly.

class transaction.interfaces.TransactionError[source]

An error occurred due to normal transaction processing.

class transaction.interfaces.TransactionFailedError[source]

Cannot perform an operation on a transaction that previously failed.

An attempt was made to commit a transaction, or to join a transaction, but this transaction previously raised an exception during an attempt to commit it. The transaction must be explicitly aborted, either by invoking abort() on the transaction, or begin() on its transaction manager.

class transaction.interfaces.DoomedTransaction[source]

A commit was attempted on a transaction that was doomed.

class transaction.interfaces.TransientError[source]

An error has occured when performing a transaction.

It’s possible that retrying the transaction will succeed.

API Objects

class transaction._transaction.Transaction(synchronizers=None, manager=None)[source]
isDoomed()[source]

See ITransaction.

doom()[source]

See ITransaction.

join(resource)[source]

See ITransaction.

savepoint(optimistic=False)[source]

See ITransaction.

register(obj)[source]

See ITransaction.

commit()[source]

See ITransaction.

getBeforeCommitHooks()[source]

See ITransaction.

addBeforeCommitHook(hook, args=(), kws=None)[source]

See ITransaction.

getAfterCommitHooks()[source]

See ITransaction.

addAfterCommitHook(hook, args=(), kws=None)[source]

See ITransaction.

abort()[source]

See ITransaction.

note(text)[source]

See ITransaction.

setUser(user_name, path='/')[source]

See ITransaction.

setExtendedInfo(name, value)[source]

See ITransaction.

class transaction._transaction.Savepoint(transaction, optimistic, *resources)[source]

Transaction savepoint.

Transaction savepoints coordinate savepoints for data managers participating in a transaction.

rollback()[source]

See ISavepoint.

class transaction._manager.TransactionManager[source]
__enter__()

Alias for get()

__exit__(t, v, tb)[source]

On error, aborts the current transaction. Otherwise, commits.

begin()[source]

See ITransactionManager.

get()[source]

See ITransactionManager.

registerSynch(synch)[source]

See ITransactionManager.

unregisterSynch(synch)[source]

See ITransactionManager.

isDoomed()[source]

See ITransactionManager.

doom()[source]

See ITransactionManager.

commit()[source]

See ITransactionManager.

abort()[source]

See ITransactionManager.

savepoint(optimistic=False)[source]

See ITransactionManager.

class transaction._manager.ThreadTransactionManager[source]

Thread-aware transaction manager.

Each thread is associated with a unique transaction.

Table Of Contents

Previous topic

Writing a Resource Manager

This Page

transaction-1.4.3/docs/_build/html/searchindex.js0000664000175000017500000002657112312641212021744 0ustar tseavertseaverSearch.setIndex({envversion:42,objtypes:{"0":"py:module","1":"py:method","2":"py:interface","3":"py:class","4":"py:attribute"},terms:{seriou:6,critic:0,loop:6,variabl:0,look:[0,2],been:[3,1,5,6,7],els:[0,2,6],cannot:[1,6],add:[4,6,7],delai:4,"public":[1,6],gener:[0,2,1],xxx:0,more:[1,3,7,5,6,4],zope:[1,6,4],our:[2,3,5],immut:2,order:[6,7],phase:[5,6],transactionmanag:[6,7],dummyfil:7,same:[3,1,5,6,7],def:[0,2,1,7],traceback:[0,2,3,1,5],backward:4,del:7,proper:0,much:[3,5],method:[0,6],avail:4,samplesavepointdatamanag:[0,2],keyword:[0,6],getaftercommithook:[6,7],valid:[2,1,6],editview:1,modifi:6,level:[2,4,6,7],optimist:6,strip:6,isn:[0,3,5,6],produc:6,both:[3,5,4],protocol:[3,5,6],truncat:6,http:0,evolv:4,"_tb_buffer":7,demonstr:2,"import":[0,1,2,3,5,7],tool:[3,5],modif:6,preced:4,exist:6,sampledatamanag:2,algorithm:6,associ:[3,5,6,7],datamanag:[1,5,6],whitespac:6,via:6,thei:[6,7],match:[3,5],succeed:6,activ:[1,6,4],total:[1,6],storag:[2,6],guarante:6,clear:2,thu:1,outsid:[0,6],similarli:2,previous:[2,6],messag:[2,1,7],unsupport:2,thi:[0,1,2,3,4,5,6,7],clean:[3,1],expect:[3,6],conveni:6,you:[0,4],idatamanag:[1,6],pass:[0,2,3,4,5,6,7],shouldn:0,instanc:[0,3,6],subroutin:2,can:[0,1,2,3,4,5,6,7],about:[0,6],propag:0,caus:[2,6],maintain:6,output:2,everi:[6,4],consist:6,normal:[2,6],alreadi:5,pair:6,especi:0,wrap:0,no_kw1:7,no_kw2:7,need:[0,3,1,5,6],"_p_jar":4,func_nam:7,which:[3,1,5,6],responsibilti:1,accept:2,set:[3,5,6,4],deadlock:6,mai:[1,6],meta:6,layer:4,specif:[1,4],defin:[0,2,6,7],handl:6,failur:[0,1,4,7],point:[2,3,1,5],toward:4,last:[0,1,2,3,5,6],python:[0,6],notion:6,entir:[2,3,5],balanc:2,fail:[0,2,3,7,6,4],version:4,within:[2,3,1,5],discard:[3,5,6],understand:2,format:6,abort:6,control:0,invalidsavepointrollbackerror:[2,6],model:[3,5],error:[0,1,5,6,7],assign:0,sometransientexcept:0,complet:[3,5,6],document:0,howev:[2,1],count:1,"_txn":7,directli:[0,1],where:[2,1],how:[0,2,1],old:1,data:[0,6],abbrevi:0,than:[3,1,5,4],constructor:6,"__exit__":6,open:1,addit:6,check:4,let:[2,7],made:[2,1,6],unregistersynch:6,also:[0,1,3,5,6,7],len:7,uncommit:[2,4],setus:6,oper:[1,2,3,5,6,4],act:1,first:[1,2,3,5,6,4],"_transact":[6,7],instead:6,resourcemanag:3,docstr:5,didn:2,write:0,redund:2,again:[0,1],todo:4,helper:0,append:[6,7],beforecommithook:6,remov:6,typic:[0,6],possibl:[2,6],process:6,each:[2,6,4],text:6,obj:6,other:[0,2,3,5,6,7],"_itransaction__hook":6,ellipsi:0,kw1:7,fals:[4,1,6,7],all:[1,2,3,4,5,6,7],user:[2,6],otherwis:6,transactionerror:6,should:[0,1,2,3,5,6],contain:[3,1,5],repres:[3,5,6,4],surround:6,full:7,dict:7,tri:[0,2],support:6,contin:0,flat:2,"try":[0,2,1],packag:0,start:[1,3,7,5,6,4],recurs:[6,7],subtransact:2,provid:[0,2,3,5,6],assert:1,programm:1,inc:[3,5],vote:[3,6,4],prevent:6,identifi:6,test__manag:7,zodb4:4,side:1,piec:3,failingdatamanag:7,ugli:0,abov:7,sue:2,str:[0,2,6,7],place:1,sub:7,browser:1,print:[0,2],differ:6,last_not:0,index:4,respons:[3,5,6],idatamanagersavepoint:6,dataobject:7,sinc:[6,7],free:2,inde:7,system:[0,2],when:[1,2,3,4,5,6,7],tent:[3,5],picklabl:6,argument:[4,6,7],roll:[2,3,5,6],after:[1,5,6,7],explain:4,record:6,"__init__":1,success:[2,6,4],becom:[2,6],doomedtransactionexcept:6,thread:6,from:[1,2,3,5,6,7],commit:[0,6],send:1,entri:2,itself:[0,7],prepar:6,exampl:[0,1,2,3,5,6,4],two:[5,6],chang:[1,2,3,5,6,4],current:[1,6,4],manag:[0,6],chanc:[3,6],"default":[0,6],transaction_manag:6,addaftercommithook:6,save:[2,3,5,6],deposit:2,app:1,name:[0,2,1,6],uncertain:2,txn:[3,1,4,7],typeerror:[2,3,5,7],post:4,intermedi:2,leav:1,confirm:6,err:7,necessari:1,hook:[1,6],far:0,lead:4,invalid:[3,5,6,4],longer:[3,5,6,7],progress:[2,6],effici:[3,5],debit:2,itransact:[3,6],nameerror:0,unchang:1,exactli:1,oop:7,persist:[6,4],overcom:0,interven:5,have:[1,2,3,4,5,6,7],illustr:[3,5],reason:[0,2],registr:[6,7],read:[2,6],someth:0,rec:7,abl:[2,1],forget:6,involv:[6,4],major:6,newlin:6,page:4,ntry:0,execut:[3,4,5,6,7],issue12022:0,"true":[0,4,1,6,7],although:6,time:[0,2,1,4],wai:[1,2,3,5,6,7],indic:6,befor:[5,6,7],perman:[3,5],explictli:6,doe:[0,6,7],threadtransactionmanag:6,show:0,awar:6,doc:[3,6],beforecomplet:[6,4],don:[0,2,3,5,6,7],recent:[0,2,3,1,5],termin:6,search:4,revert:[3,5],limit:6,"new":[2,1,6],registersynch:6,awai:[2,3,5,6],assertraisesex:7,"throw":[3,5],probabl:1,isdoom:[1,6],string:[2,3,5,6],particip:[6,4],iff:4,mapper:1,due:6,tpc_vote:[6,4],attempt:[0,1,3,5,6,4],must:[3,1,5,6,4],"while":[3,5,7],contriv:0,extens:6,event:6,path:6,end:6,sequenc:[2,6],dummi:7,assumpt:0,isavepointdatamanag:6,extend:6,across:6,could:[3,1,5],"transient":0,block:0,behavior:6,either:[1,6,4],global:6,transienterror:[0,6],mgr:7,savepoint:6,proce:2,attr_count:1,meant:[3,5],unregist:6,common:7,abort_sub:4,transactionfailederror:[2,6],reset:[3,5],expens:1,now:[0,2,3,5,7],succe:[0,6,4],apply_entri:2,signal:0,note:[0,6],get:[2,1,6],access_count:1,perform:6,alwai:[3,1,5],hookrais:7,occur:[2,1,6,4],dm2:[0,1],want:[0,1,4],number:[0,2,1],know:0,asynchron:4,non:[1,6,4],continu:1,still:[1,4],might:4,applic:[0,1,3,5,6,4],mainli:5,launch:4,affect:5,slate:3,previou:2,atom:2,resourc:[4,5,6,7],care:6,iter:[0,6],make:[0,2,6],detect:[5,6],detail:6,descript:6,join:[4,1,6,7],partial:[2,3,5],impos:6,memori:2,becaus:[2,6,4],undefin:6,done:[1,2,3,5,6,7],desir:6,"final":6,synchron:6,lifetim:6,avoid:[1,6],"__enter__":6,itransactionmanag:6,tripl:6,top:[2,6,4],distinct:6,never:[3,6],too:6,empti:6,them:[1,4,7],omplet:5,freed:2,connect:6,delta:[3,5],doomabl:1,allow:[2,3,5],real:7,individu:4,onc:[3,5,4],later:[2,3,5,6],veri:2,dictionari:6,tell:0,consid:6,modul:[1,4],qualifi:6,content:4,tpc_abort:[6,4],"class":[0,1,3,5,6,7],synch:6,infinit:6,"_manag":[6,7],sychron:4,onli:[4,6,7],list:[0,4,7],whether:[6,4],forc:1,stale:[3,5],form:1,addbeforecommithook:6,deprec:6,purpos:2,notifi:6,"_compat":7,without:[3,5,4],abandon:6,no_arg:7,often:[0,3,5],circumst:1,unexpect:2,four:4,getbeforecommithook:[6,7],effect:1,reflect:[3,5],doom:6,begin:[1,6,7],tupl:6,appli:2,doesn:0,flag:2,here:[0,1],savepoint2:2,savepoint1:2,bug:0,calcul:1,coordin:6,life:6,should_retri:0,realli:[0,3,5],secondari:2,savepointsampl:[0,2],fake:7,valu:[2,1,6,4],stub:1,"boolean":[6,4],abil:[3,5],retri:6,yet:6,self:[0,1,7],properti:6,implement:[2,3,1,5,4],intend:6,earlier:[2,3,5,6],kei:[0,6],cleanup:7,invok:6,behav:[1,6],includ:[2,6,4],interact:4,"return":[0,1,6,7],isynchron:6,take:[0,2,6,4],credit:2,rather:[0,1,4],through:[3,5],rec1:7,relat:[1,6],sql:1,aftercomplet:[6,4],boundari:[0,6],enter:2,conflict:[0,6],until:[2,3,5,4],textual:6,overdrawn:2,rec2:7,rec3:7,rec0:7,even:7,sever:7,org:0,rollback:[2,3,5,6],cours:[0,3,5],establish:6,arg:[6,7],posit:6,updat:[2,3,5],consum:[6,7],call:[0,1,2,3,4,5,6,7],regist:[4,6,7],sourc:6,uniqu:6,what:0,entry_savepoint:2,"case":[0,1,6,4],tpc_begin:[4,6,7],trivial:[3,5],pre:[1,4],substransact:4,disk:2,ani:[0,1,2,3,5,6,4],"__getattr__":1,found:1,creat:[2,3,1,5,6],outer:2,line:2,implicitli:1,explicitli:6,bit:5,rang:0,fund:2,correct:2,lazi:[3,5],doctest:0,follow:[1,4],missmatch:[3,5],provok:7,statu:[2,4,7],someon:4,tpc_finish:[6,4],see:[0,1,6,7],dure:[2,1,6,4],zodb:[6,4],state:[2,3,5,4],isavepoint:6,further:6,outstand:1,multipl:[2,6],kw2:7,salli:2,statement:0,stage:[3,5],dm_no_sp:2,account:2,preceed:[3,5],anyth:7,notif:6,some:[0,2,3,5,6,4],sampl:[2,3,5],between:4,adapt:4,amount:2,databas:[1,6,4],setextendedinfo:6,sometim:[0,4],initi:[2,3,5],would:6,addaftercommit:6,callabl:4,work:[0,1,2,3,5,6],word:[3,5],ask:[3,1],transat:[3,5],request:1,exceed:6,harmless:2,mani:0,creation:6,safe:1,like:[0,2,3,1,5],reset_log:7,log:7,buffer:7,except:[0,1,2,3,6,7],doomedtransact:[1,6],were:6,sam:2,attribut:[6,4],type:6,sort:6,unsaf:1,newtransact:6,rais:[0,1,2,3,6,7],alia:6,toler:2,rejoin:6,whenev:7,sortkei:[1,6],come:2,invari:4,successful:4,validate_account:2,given:[3,5],tranasct:4,"function":[2,6],specifi:6,bob:2,valueerror:[0,2,3,1],run:[1,6],test:[0,2,3,5,7],program:1,verifi:[3,6],commitfailur:7,subclass:0,got:2,most:[0,2,3,1,5],peopl:2,primari:2,none:[3,6,7],back:[2,3,5,6],size:6,depend:[4,7],addbeforecommit:6,user_nam:6,code:[2,1,6,4],monkei:7,"break":0},filenames:["convenience","doom","savepoint","resourcemanager","index","datamanager","api","hooks"],objnames:{"0":["py","module","Python module"],"1":["py","method","Python method"],"2":["py","interface","interface"],"3":["py","class","Python class"],"4":["py","attribute","Python attribute"]},titleterms:{tpc_vote:3,two:4,abort:5,manag:[3,5],interfac:6,failur:2,without:2,tabl:4,applic:2,addaftercommithook:7,retri:0,conveni:0,document:4,doom:1,error:4,indic:4,befor:4,data:5,api:6,phase:4,resourc:3,object:6,machineri:7,hook:[4,7],support:[0,2],tpc_abort:3,issu:4,refer:6,write:[3,5],invalid:2,transact:[0,4,1,6,7],handl:4,prepar:5,after:4,compat:4,savepoint:[2,3,5],method:[3,5,7],tpc_begin:3,synchron:4,tpc_finish:3,databas:2,commit:[5,4],simpl:[3,5],addbeforecommithook:7},objects:{"transaction.interfaces":{ITransactionManager:[6,2,1,""],ISavepoint:[6,2,1,""],IDataManagerSavepoint:[6,2,1,""],ITransaction:[6,2,1,""],TransientError:[6,3,1,""],TransactionFailedError:[6,3,1,""],InvalidSavepointRollbackError:[6,3,1,""],DoomedTransaction:[6,3,1,""],ISynchronizer:[6,2,1,""],ISavepointDataManager:[6,2,1,""],TransactionError:[6,3,1,""],IDataManager:[6,2,1,""]},"transaction.interfaces.ISavepointDataManager":{savepoint:[6,1,1,""]},"transaction.interfaces.ITransactionManager":{unregisterSynch:[6,1,1,""],abort:[6,1,1,""],isDoomed:[6,1,1,""],doom:[6,1,1,""],begin:[6,1,1,""],get:[6,1,1,""],savepoint:[6,1,1,""],commit:[6,1,1,""],registerSynch:[6,1,1,""]},"transaction._transaction.Savepoint":{rollback:[6,1,1,""]},"transaction.interfaces.IDataManagerSavepoint":{rollback:[6,1,1,""]},"transaction.interfaces.ISavepoint":{rollback:[6,1,1,""],valid:[6,4,1,""]},"transaction._transaction.Transaction":{addBeforeCommitHook:[6,1,1,""],join:[6,1,1,""],abort:[6,1,1,""],getBeforeCommitHooks:[6,1,1,""],register:[6,1,1,""],isDoomed:[6,1,1,""],addAfterCommitHook:[6,1,1,""],savepoint:[6,1,1,""],commit:[6,1,1,""],getAfterCommitHooks:[6,1,1,""],setUser:[6,1,1,""],note:[6,1,1,""],setExtendedInfo:[6,1,1,""],doom:[6,1,1,""]},"transaction.interfaces.ISynchronizer":{newTransaction:[6,1,1,""],afterCompletion:[6,1,1,""],beforeCompletion:[6,1,1,""]},"transaction._manager.TransactionManager":{"__exit__":[6,1,1,""],unregisterSynch:[6,1,1,""],abort:[6,1,1,""],isDoomed:[6,1,1,""],doom:[6,1,1,""],begin:[6,1,1,""],get:[6,1,1,""],savepoint:[6,1,1,""],commit:[6,1,1,""],"__enter__":[6,1,1,""],registerSynch:[6,1,1,""]},transaction:{"_transaction":[6,0,0,"-"],interfaces:[6,0,0,"-"],"_manager":[6,0,0,"-"]},"transaction._manager":{ThreadTransactionManager:[6,3,1,""],TransactionManager:[6,3,1,""]},"transaction._transaction":{Savepoint:[6,3,1,""],Transaction:[6,3,1,""]},"transaction.interfaces.ITransaction":{addBeforeCommitHook:[6,1,1,""],join:[6,1,1,""],abort:[6,1,1,""],getBeforeCommitHooks:[6,1,1,""],user:[6,4,1,""],doom:[6,1,1,""],savepoint:[6,1,1,""],commit:[6,1,1,""],getAfterCommitHooks:[6,1,1,""],setUser:[6,1,1,""],note:[6,1,1,""],setExtendedInfo:[6,1,1,""],description:[6,4,1,""],addAfterCommitHook:[6,1,1,""],beforeCommitHook:[6,1,1,""]},"transaction.interfaces.IDataManager":{tpc_vote:[6,1,1,""],abort:[6,1,1,""],tpc_begin:[6,1,1,""],sortKey:[6,1,1,""],tpc_finish:[6,1,1,""],tpc_abort:[6,1,1,""],commit:[6,1,1,""],transaction_manager:[6,4,1,""]}},titles:["Transaction convenience support","Dooming Transactions","Savepoints","Writing a Resource Manager","transaction Documentation","Writing a Data Manager","transaction API Reference","Hooking the Transaction Machinery"]})transaction-1.4.3/docs/_build/html/index.html0000664000175000017500000003314412312641212021100 0ustar tseavertseaver transaction Documentation — transaction 1.2 documentation

transaction Documentation

Transaction objects manage resources for an individual activity.

Compatibility issues

The implementation of Transaction objects involves two layers of backwards compatibility, because this version of transaction supports both ZODB 3 and ZODB 4. Zope is evolving towards the ZODB4 interfaces.

Transaction has two methods for a resource manager to call to participate in a transaction – register() and join(). join() takes a resource manager and adds it to the list of resources. register() is for backwards compatibility. It takes a persistent object and registers its _p_jar attribute. TODO: explain adapter

Two-phase commit

A transaction commit involves an interaction between the transaction object and one or more resource managers. The transaction manager calls the following four methods on each resource manager; it calls tpc_begin() on each resource manager before calling commit() on any of them.

  1. tpc_begin(txn)
  2. commit(txn)
  3. tpc_vote(txn)
  4. tpc_finish(txn)

Before-commit hook

Sometimes, applications want to execute some code when a transaction is committed. For example, one might want to delay object indexing until a transaction commits, rather than indexing every time an object is changed. Or someone might want to check invariants only after a set of operations. A pre-commit hook is available for such use cases: use addBeforeCommitHook(), passing it a callable and arguments. The callable will be called with its arguments at the start of the commit (but not for substransaction commits).

After-commit hook

Sometimes, applications want to execute code after a transaction commit attempt succeeds or aborts. For example, one might want to launch non transactional code after a successful commit. Or still someone might want to launch asynchronous code after. A post-commit hook is available for such use cases: use addAfterCommitHook(), passing it a callable and arguments. The callable will be called with a Boolean value representing the status of the commit operation as first argument (true if successfull or false iff aborted) preceding its arguments at the start of the commit (but not for substransaction commits). Commit hooks are not called for transaction.abort().

Error handling

When errors occur during two-phase commit, the transaction manager aborts all the resource managers. The specific methods it calls depend on whether the error occurs before or after the call to tpc_vote() on that transaction manager.

If the resource manager has not voted, then the resource manager will have one or more uncommitted objects. There are two cases that lead to this state; either the transaction manager has not called commit() for any objects on this resource manager or the call that failed was a commit() for one of the objects of this resource manager. For each uncommitted object, including the object that failed in its commit(), call abort().

Once uncommitted objects are aborted, tpc_abort() or abort_sub() is called on each resource manager.

Synchronization

You can register sychronization objects (synchronizers) with the tranasction manager. The synchronizer must implement beforeCompletion() and afterCompletion() methods. The transaction manager calls beforeCompletion() when it starts a top-level two-phase commit. It calls afterCompletion() when a top-level transaction is committed or aborted. The methods are passed the current Transaction as their only argument.

Contents:

Indices and tables

transaction-1.4.3/docs/_build/html/datamanager.html0000664000175000017500000010103312312641212022226 0ustar tseavertseaver Writing a Data Manager — transaction 1.2 documentation

Writing a Data Manager

Simple Data Manager

>>> from transaction.tests.examples import DataManager

This transaction.tests.examples.DataManager class provides a trivial data-manager implementation and docstrings to illustrate the the protocol and to provide a tool for writing tests.

Our sample data manager has state that is updated through an inc method and through transaction operations.

When we create a sample data manager:

>>> dm = DataManager()

It has two bits of state, state:

>>> dm.state
0

and delta:

>>> dm.delta
0

Both of which are initialized to 0. state is meant to model committed state, while delta represents tentative changes within a transaction. We change the state by calling inc:

>>> dm.inc()

which updates delta:

>>> dm.delta
1

but state isn’t changed until we commit the transaction:

>>> dm.state
0

To commit the changes, we use 2-phase commit. We execute the first stage by calling prepare. We need to pass a transation. Our sample data managers don’t really use the transactions for much, so we’ll be lazy and use strings for transactions:

>>> t1 = '1'
>>> dm.prepare(t1)

The sample data manager updates the state when we call prepare:

>>> dm.state
1
>>> dm.delta
1

This is mainly so we can detect some affect of calling the methods.

Now if we call commit:

>>> dm.commit(t1)

Our changes are”permanent”. The state reflects the changes and the delta has been reset to 0.

>>> dm.state
1
>>> dm.delta
0

The prepare() Method

Prepare to commit data

>>> dm = DataManager()
>>> dm.inc()
>>> t1 = '1'
>>> dm.prepare(t1)
>>> dm.commit(t1)
>>> dm.state
1
>>> dm.inc()
>>> t2 = '2'
>>> dm.prepare(t2)
>>> dm.abort(t2)
>>> dm.state
1

It is en error to call prepare more than once without an intervening commit or abort:

>>> dm.prepare(t1)

>>> dm.prepare(t1)
Traceback (most recent call last):
...
TypeError: Already prepared

>>> dm.prepare(t2)
Traceback (most recent call last):
...
TypeError: Already prepared

>>> dm.abort(t1)

If there was a preceeding savepoint, the transaction must match:

>>> rollback = dm.savepoint(t1)
>>> dm.prepare(t2)
Traceback (most recent call last):
,,,
TypeError: ('Transaction missmatch', '2', '1')

>>> dm.prepare(t1)

The abort() method

The abort method can be called before two-phase commit to throw away work done in the transaction:

>>> dm = DataManager()
>>> dm.inc()
>>> dm.state, dm.delta
(0, 1)
>>> t1 = '1'
>>> dm.abort(t1)
>>> dm.state, dm.delta
(0, 0)

The abort method also throws away work done in savepoints:

>>> dm.inc()
>>> r = dm.savepoint(t1)
>>> dm.inc()
>>> r = dm.savepoint(t1)
>>> dm.state, dm.delta
(0, 2)
>>> dm.abort(t1)
>>> dm.state, dm.delta
(0, 0)

If savepoints are used, abort must be passed the same transaction:

>>> dm.inc()
>>> r = dm.savepoint(t1)
>>> t2 = '2'
>>> dm.abort(t2)
Traceback (most recent call last):
...
TypeError: ('Transaction missmatch', '2', '1')

>>> dm.abort(t1)

The abort method is also used to abort a two-phase commit:

>>> dm.inc()
>>> dm.state, dm.delta
(0, 1)
>>> dm.prepare(t1)
>>> dm.state, dm.delta
(1, 1)
>>> dm.abort(t1)
>>> dm.state, dm.delta
(0, 0)

Of course, the transactions passed to prepare and abort must match:

>>> dm.prepare(t1)
>>> dm.abort(t2)
Traceback (most recent call last):
...
TypeError: ('Transaction missmatch', '2', '1')

>>> dm.abort(t1)

The commit() method

Called to omplete two-phase commit

>>> dm = DataManager()
>>> dm.state
0
>>> dm.inc()

We start two-phase commit by calling prepare:

>>> t1 = '1'
>>> dm.prepare(t1)

We complete it by calling commit:
>>> dm.commit(t1)
>>> dm.state
1

It is an error ro call commit without calling prepare first:

>>> dm.inc()
>>> t2 = '2'
>>> dm.commit(t2)
Traceback (most recent call last):
...
TypeError: Not prepared to commit

>>> dm.prepare(t2)
>>> dm.commit(t2)

If course, the transactions given to prepare and commit must be the same:

>>> dm.inc()
>>> t3 = '3'
>>> dm.prepare(t3)
>>> dm.commit(t2)
Traceback (most recent call last):
...
TypeError: ('Transaction missmatch', '2', '3')

The savepoint() method

Provide the ability to rollback transaction state

Savepoints provide a way to:

  • Save partial transaction work. For some data managers, this could allow resources to be used more efficiently.
  • Provide the ability to revert state to a point in a transaction without aborting the entire transaction. In other words, savepoints support partial aborts.

Savepoints don’t use two-phase commit. If there are errors in setting or rolling back to savepoints, the application should abort the containing transaction. This is not the responsibility of the data manager.

Savepoints are always associated with a transaction. Any work done in a savepoint’s transaction is tentative until the transaction is committed using two-phase commit.

>>> dm = DataManager()
>>> dm.inc()
>>> t1 = '1'
>>> r = dm.savepoint(t1)
>>> dm.state, dm.delta
(0, 1)
>>> dm.inc()
>>> dm.state, dm.delta
(0, 2)
>>> r.rollback()
>>> dm.state, dm.delta
(0, 1)
>>> dm.prepare(t1)
>>> dm.commit(t1)
>>> dm.state, dm.delta
(1, 0)

Savepoints must have the same transaction:

>>> r1 = dm.savepoint(t1)
>>> dm.state, dm.delta
(1, 0)
>>> dm.inc()
>>> dm.state, dm.delta
(1, 1)
>>> t2 = '2'
>>> r2 = dm.savepoint(t2)
Traceback (most recent call last):
...
TypeError: ('Transaction missmatch', '2', '1')

>>> r2 = dm.savepoint(t1)
>>> dm.inc()
>>> dm.state, dm.delta
(1, 2)

If we rollback to an earlier savepoint, we discard all work done later:

>>> r1.rollback()
>>> dm.state, dm.delta
(1, 0)

and we can no longer rollback to the later savepoint:

>>> r2.rollback()
Traceback (most recent call last):
...
TypeError: ('Attempt to roll back to invalid save point', 3, 2)

We can roll back to a savepoint as often as we like:

>>> r1.rollback()
>>> r1.rollback()
>>> r1.rollback()
>>> dm.state, dm.delta
(1, 0)

>>> dm.inc()
>>> dm.inc()
>>> dm.inc()
>>> dm.state, dm.delta
(1, 3)
>>> r1.rollback()
>>> dm.state, dm.delta
(1, 0)

But we can’t rollback to a savepoint after it has been committed:

>>> dm.prepare(t1)
>>> dm.commit(t1)

>>> r1.rollback()
Traceback (most recent call last):
...
TypeError: Attempt to rollback stale rollback

Table Of Contents

Previous topic

Hooking the Transaction Machinery

Next topic

Writing a Resource Manager

This Page

transaction-1.4.3/docs/_build/html/savepoint.html0000664000175000017500000007656712312641212022021 0ustar tseavertseaver Savepoints — transaction 1.2 documentation

Savepoints

Savepoints provide a way to save to disk intermediate work done during a transaction allowing:

  • partial transaction (subtransaction) rollback (abort)
  • state of saved objects to be freed, freeing on-line memory for other uses

Savepoints make it possible to write atomic subroutines that don’t make top-level transaction commitments.

Applications

To demonstrate how savepoints work with transactions, we’ve provided a sample data manager implementation that provides savepoint support. The primary purpose of this data manager is to provide code that can be read to understand how savepoints work. The secondary purpose is to provide support for demonstrating the correct operation of savepoint support within the transaction system. This data manager is very simple. It provides flat storage of named immutable values, like strings and numbers.

>>> import transaction
>>> from transaction.tests import savepointsample
>>> dm = savepointsample.SampleSavepointDataManager()
>>> dm['name'] = 'bob'

As with other data managers, we can commit changes:

>>> transaction.commit()
>>> dm['name']
'bob'

and abort changes:

>>> dm['name'] = 'sally'
>>> dm['name']
'sally'
>>> transaction.abort()
>>> dm['name']
'bob'

Now, let’s look at an application that manages funds for people. It allows deposits and debits to be entered for multiple people. It accepts a sequence of entries and generates a sequence of status messages. For each entry, it applies the change and then validates the user’s account. If the user’s account is invalid, we roll back the change for that entry. The success or failure of an entry is indicated in the output status. First we’ll initialize some accounts:

>>> dm['bob-balance'] = 0.0
>>> dm['bob-credit'] = 0.0
>>> dm['sally-balance'] = 0.0
>>> dm['sally-credit'] = 100.0
>>> transaction.commit()

Now, we’ll define a validation function to validate an account:

>>> def validate_account(name):
...     if dm[name+'-balance'] + dm[name+'-credit'] < 0:
...         raise ValueError('Overdrawn', name)

And a function to apply entries. If the function fails in some unexpected way, it rolls back all of its changes and prints the error:

>>> def apply_entries(entries):
...     savepoint = transaction.savepoint()
...     try:
...         for name, amount in entries:
...             entry_savepoint = transaction.savepoint()
...             try:
...                 dm[name+'-balance'] += amount
...                 validate_account(name)
...             except ValueError as error:
...                 entry_savepoint.rollback()
...                 print("%s %s" % ('Error', str(error)))
...             else:
...                 print("%s %s" % ('Updated', name))
...     except Exception as error:
...         savepoint.rollback()
...         print("%s" % ('Unexpected exception'))

Now let’s try applying some entries:

>>> apply_entries([
...     ('bob',   10.0),
...     ('sally', 10.0),
...     ('bob',   20.0),
...     ('sally', 10.0),
...     ('bob',   -100.0),
...     ('sally', -100.0),
...     ])
Updated bob
Updated sally
Updated bob
Updated sally
Error ('Overdrawn', 'bob')
Updated sally

>>> dm['bob-balance']
30.0

>>> dm['sally-balance']
-80.0

If we provide entries that cause an unexpected error:

>>> apply_entries([
...     ('bob',   10.0),
...     ('sally', 10.0),
...     ('bob',   '20.0'),
...     ('sally', 10.0),
...     ])
Updated bob
Updated sally
Unexpected exception

Because the apply_entries used a savepoint for the entire function, it was able to rollback the partial changes without rolling back changes made in the previous call to apply_entries:

>>> dm['bob-balance']
30.0

>>> dm['sally-balance']
-80.0

If we now abort the outer transactions, the earlier changes will go away:

>>> transaction.abort()

>>> dm['bob-balance']
0.0

>>> dm['sally-balance']
0.0

Savepoint invalidation

A savepoint can be used any number of times:

>>> dm['bob-balance'] = 100.0
>>> dm['bob-balance']
100.0
>>> savepoint = transaction.savepoint()

>>> dm['bob-balance'] = 200.0
>>> dm['bob-balance']
200.0
>>> savepoint.rollback()
>>> dm['bob-balance']
100.0

>>> savepoint.rollback()  # redundant, but should be harmless
>>> dm['bob-balance']
100.0

>>> dm['bob-balance'] = 300.0
>>> dm['bob-balance']
300.0
>>> savepoint.rollback()
>>> dm['bob-balance']
100.0

However, using a savepoint invalidates any savepoints that come after it:

>>> dm['bob-balance'] = 200.0
>>> dm['bob-balance']
200.0
>>> savepoint1 = transaction.savepoint()

>>> dm['bob-balance'] = 300.0
>>> dm['bob-balance']
300.0
>>> savepoint2 = transaction.savepoint()

>>> savepoint.rollback()
>>> dm['bob-balance']
100.0

>>> savepoint2.rollback() 
Traceback (most recent call last):
...
InvalidSavepointRollbackError: invalidated by a later savepoint

>>> savepoint1.rollback() 
Traceback (most recent call last):
...
InvalidSavepointRollbackError: invalidated by a later savepoint

>>> transaction.abort()

Databases without savepoint support

Normally it’s an error to use savepoints with databases that don’t support savepoints:

>>> dm_no_sp = savepointsample.SampleDataManager()
>>> dm_no_sp['name'] = 'bob'
>>> transaction.commit()
>>> dm_no_sp['name'] = 'sally'
>>> transaction.savepoint() 
Traceback (most recent call last):
...
TypeError: ('Savepoints unsupported', {'name': 'bob'})

>>> transaction.abort()

However, a flag can be passed to the transaction savepoint method to indicate that databases without savepoint support should be tolerated until a savepoint is rolled back. This allows transactions to proceed if there are no reasons to roll back:

>>> dm_no_sp['name'] = 'sally'
>>> savepoint = transaction.savepoint(1)
>>> dm_no_sp['name'] = 'sue'
>>> transaction.commit()
>>> dm_no_sp['name']
'sue'

>>> dm_no_sp['name'] = 'sam'
>>> savepoint = transaction.savepoint(1)
>>> savepoint.rollback() 
Traceback (most recent call last):
...
TypeError: ('Savepoints unsupported', {'name': 'sam'})

Failures

If a failure occurs when creating or rolling back a savepoint, the transaction state will be uncertain and the transaction will become uncommitable. From that point on, most transaction operations, including commit, will fail until the transaction is aborted.

In the previous example, we got an error when we tried to rollback the savepoint. If we try to commit the transaction, the commit will fail:

>>> transaction.commit() 
Traceback (most recent call last):
...
TransactionFailedError: An operation previously failed, with traceback:
...
TypeError: ('Savepoints unsupported', {'name': 'sam'})

We have to abort it to make any progress:

>>> transaction.abort()

Similarly, in our earlier example, where we tried to take a savepoint with a data manager that didn’t support savepoints:

>>> dm_no_sp['name'] = 'sally'
>>> dm['name'] = 'sally'
>>> savepoint = transaction.savepoint() 
Traceback (most recent call last):
...
TypeError: ('Savepoints unsupported', {'name': 'sue'})

>>> transaction.commit() 
Traceback (most recent call last):
...
TransactionFailedError: An operation previously failed, with traceback:
...
TypeError: ('Savepoints unsupported', {'name': 'sue'})


>>> transaction.abort()

After clearing the transaction with an abort, we can get on with new transactions:

>>> dm_no_sp['name'] = 'sally'
>>> dm['name'] = 'sally'
>>> transaction.commit()
>>> dm_no_sp['name']
'sally'
>>> dm['name']
'sally'

Table Of Contents

Previous topic

Dooming Transactions

Next topic

Hooking the Transaction Machinery

This Page

transaction-1.4.3/docs/_build/html/doom.html0000664000175000017500000003754012312641212020733 0ustar tseavertseaver Dooming Transactions — transaction 1.2 documentation

Dooming Transactions

A doomed transaction behaves exactly the same way as an active transaction but raises an error on any attempt to commit it, thus forcing an abort.

Doom is useful in places where abort is unsafe and an exception cannot be raised. This occurs when the programmer wants the code following the doom to run but not commit. It is unsafe to abort in these circumstances as a following get() may implicitly open a new transaction.

Any attempt to commit a doomed transaction will raise a DoomedTransaction exception.

An example of such a use case can be found in zope/app/form/browser/editview.py. Here a form validation failure must doom the transaction as committing the transaction may have side-effects. However, the form code must continue to calculate a form containing the error messages to return.

For Zope in general, code running within a request should always doom transactions rather than aborting them. It is the responsibilty of the publication to either abort() or commit() the transaction. Application code can use savepoints and doom() safely.

To see how it works we first need to create a stub data manager:

>>> from transaction.interfaces import IDataManager
>>> from zope.interface import implementer
>>> @implementer(IDataManager)
... class DataManager:
...     def __init__(self):
...         self.attr_counter = {}
...     def __getattr__(self, name):
...         def f(transaction):
...             self.attr_counter[name] = self.attr_counter.get(name, 0) + 1
...         return f
...     def total(self):
...         count = 0
...         for access_count in self.attr_counter.values():
...             count += access_count
...         return count
...     def sortKey(self):
...         return 1

Start a new transaction:

>>> import transaction
>>> txn = transaction.begin()
>>> dm = DataManager()
>>> txn.join(dm)

We can ask a transaction if it is doomed to avoid expensive operations. An example of a use case is an object-relational mapper where a pre-commit hook sends all outstanding SQL to a relational database for objects changed during the transaction. This expensive operation is not necessary if the transaction has been doomed. A non-doomed transaction should return False:

>>> txn.isDoomed()
False

We can doom a transaction by calling .doom() on it:

>>> txn.doom()
>>> txn.isDoomed()
True

We can doom it again if we like:

>>> txn.doom()

The data manager is unchanged at this point:

>>> dm.total()
0

Attempting to commit a doomed transaction any number of times raises a DoomedTransaction:

>>> txn.commit() 
Traceback (most recent call last):
DoomedTransaction: transaction doomed, cannot commit
>>> txn.commit() 
Traceback (most recent call last):
DoomedTransaction: transaction doomed, cannot commit

But still leaves the data manager unchanged:

>>> dm.total()
0

But the doomed transaction can be aborted:

>>> txn.abort()

Which aborts the data manager:

>>> dm.total()
1
>>> dm.attr_counter['abort']
1

Dooming the current transaction can also be done directly from the transaction module. We can also begin a new transaction directly after dooming the old one:

>>> txn = transaction.begin()
>>> transaction.isDoomed()
False
>>> transaction.doom()
>>> transaction.isDoomed()
True
>>> txn = transaction.begin()

After committing a transaction we get an assertion error if we try to doom the transaction. This could be made more specific, but trying to doom a transaction after it’s been committed is probably a programming error:

>>> txn = transaction.begin()
>>> txn.commit()
>>> txn.doom()
Traceback (most recent call last):
    ...
ValueError: non-doomable

A doomed transaction should act the same as an active transaction, so we should be able to join it:

>>> txn = transaction.begin()
>>> txn.doom()
>>> dm2 = DataManager()
>>> txn.join(dm2)

Clean up:

>>> txn = transaction.begin()
>>> txn.abort()

Previous topic

Transaction convenience support

Next topic

Savepoints

This Page

transaction-1.4.3/docs/_build/html/_static/0000775000175000017500000000000012312641352020531 5ustar tseavertseavertransaction-1.4.3/docs/_build/html/_static/placeholder.txt0000664000175000017500000000000012073316126023544 0ustar tseavertseavertransaction-1.4.3/docs/_build/html/_static/pygments.css0000664000175000017500000000753412312641212023115 0ustar tseavertseaver.highlight .hll { background-color: #ffffcc } .highlight { background: #eeffcc; } .highlight .c { color: #408090; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #007020; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #007020 } /* Comment.Preproc */ .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #333333 } /* Generic.Output */ .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0044DD } /* Generic.Traceback */ .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #007020 } /* Keyword.Pseudo */ .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #902000 } /* Keyword.Type */ .highlight .m { color: #208050 } /* Literal.Number */ .highlight .s { color: #4070a0 } /* Literal.String */ .highlight .na { color: #4070a0 } /* Name.Attribute */ .highlight .nb { color: #007020 } /* Name.Builtin */ .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ .highlight .no { color: #60add5 } /* Name.Constant */ .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #007020 } /* Name.Exception */ .highlight .nf { color: #06287e } /* Name.Function */ .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #bb60d5 } /* Name.Variable */ .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #208050 } /* Literal.Number.Float */ .highlight .mh { color: #208050 } /* Literal.Number.Hex */ .highlight .mi { color: #208050 } /* Literal.Number.Integer */ .highlight .mo { color: #208050 } /* Literal.Number.Oct */ .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ .highlight .sc { color: #4070a0 } /* Literal.String.Char */ .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ .highlight .sx { color: #c65d09 } /* Literal.String.Other */ .highlight .sr { color: #235388 } /* Literal.String.Regex */ .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ .highlight .ss { color: #517918 } /* Literal.String.Symbol */ .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */transaction-1.4.3/docs/_build/html/_static/basic.css0000664000175000017500000002040412312641212022317 0ustar tseavertseaver/* * basic.css * ~~~~~~~~~ * * Sphinx stylesheet -- basic theme. * * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /* -- main layout ----------------------------------------------------------- */ div.clearer { clear: both; } /* -- relbar ---------------------------------------------------------------- */ div.related { width: 100%; font-size: 90%; } div.related h3 { display: none; } div.related ul { margin: 0; padding: 0 0 0 10px; list-style: none; } div.related li { display: inline; } div.related li.right { float: right; margin-right: 5px; } /* -- sidebar --------------------------------------------------------------- */ div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; } div.sphinxsidebar { float: left; width: 230px; margin-left: -100%; font-size: 90%; } div.sphinxsidebar ul { list-style: none; } div.sphinxsidebar ul ul, div.sphinxsidebar ul.want-points { margin-left: 20px; list-style: square; } div.sphinxsidebar ul ul { margin-top: 0; margin-bottom: 0; } div.sphinxsidebar form { margin-top: 10px; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } div.sphinxsidebar #searchbox input[type="text"] { width: 170px; } div.sphinxsidebar #searchbox input[type="submit"] { width: 30px; } img { border: 0; max-width: 100%; } /* -- search page ----------------------------------------------------------- */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* -- index page ------------------------------------------------------------ */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* -- general index --------------------------------------------------------- */ table.indextable { width: 100%; } table.indextable td { text-align: left; vertical-align: top; } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } div.modindex-jumpbox { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 1em 0 1em 0; padding: 0.4em; } div.genindex-jumpbox { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 1em 0 1em 0; padding: 0.4em; } /* -- general body styles --------------------------------------------------- */ a.headerlink { visibility: hidden; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } div.body p.caption { text-align: inherit; } div.body td { text-align: left; } .field-list ul { padding-left: 1em; } .first { margin-top: 0 !important; } p.rubric { margin-top: 30px; font-weight: bold; } img.align-left, .figure.align-left, object.align-left { clear: left; float: left; margin-right: 1em; } img.align-right, .figure.align-right, object.align-right { clear: right; float: right; margin-left: 1em; } img.align-center, .figure.align-center, object.align-center { display: block; margin-left: auto; margin-right: auto; } .align-left { text-align: left; } .align-center { text-align: center; } .align-right { text-align: right; } /* -- sidebars -------------------------------------------------------------- */ div.sidebar { margin: 0 0 0.5em 1em; border: 1px solid #ddb; padding: 7px 7px 0 7px; background-color: #ffe; width: 40%; float: right; } p.sidebar-title { font-weight: bold; } /* -- topics ---------------------------------------------------------------- */ div.topic { border: 1px solid #ccc; padding: 7px 7px 0 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* -- admonitions ----------------------------------------------------------- */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; } div.admonition dt { font-weight: bold; } div.admonition dl { margin-bottom: 0; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } div.body p.centered { text-align: center; margin-top: 25px; } /* -- tables ---------------------------------------------------------------- */ table.docutils { border: 0; border-collapse: collapse; } table.docutils td, table.docutils th { padding: 1px 8px 1px 5px; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #aaa; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } th { text-align: left; padding-right: 5px; } table.citation { border-left: solid 1px gray; margin-left: 1px; } table.citation td { border-bottom: none; } /* -- other body styles ----------------------------------------------------- */ ol.arabic { list-style: decimal; } ol.loweralpha { list-style: lower-alpha; } ol.upperalpha { list-style: upper-alpha; } ol.lowerroman { list-style: lower-roman; } ol.upperroman { list-style: upper-roman; } dl { margin-bottom: 15px; } dd p { margin-top: 0px; } dd ul, dd table { margin-bottom: 10px; } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px; } dt:target, .highlighted { background-color: #fbe54e; } dl.glossary dt { font-weight: bold; font-size: 1.1em; } .field-list ul { margin: 0; padding-left: 1em; } .field-list p { margin: 0; } .optional { font-size: 1.3em; } .versionmodified { font-style: italic; } .system-message { background-color: #fda; padding: 5px; border: 3px solid red; } .footnote:target { background-color: #ffa; } .line-block { display: block; margin-top: 1em; margin-bottom: 1em; } .line-block .line-block { margin-top: 0; margin-bottom: 0; margin-left: 1.5em; } .guilabel, .menuselection { font-family: sans-serif; } .accelerator { text-decoration: underline; } .classifier { font-style: oblique; } abbr, acronym { border-bottom: dotted 1px; cursor: help; } /* -- code displays --------------------------------------------------------- */ pre { overflow: auto; overflow-y: hidden; /* fixes display issues on Chrome browsers */ } td.linenos pre { padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { margin-left: 0.5em; } table.highlighttable td { padding: 0 0.5em 0 0.5em; } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; } tt.descclassname { background-color: transparent; } tt.xref, a tt { background-color: transparent; font-weight: bold; } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } .viewcode-link { float: right; } .viewcode-back { float: right; font-family: sans-serif; } div.viewcode-block:target { margin: -1px -10px; padding: 0 10px; } /* -- math display ---------------------------------------------------------- */ img.math { vertical-align: middle; } div.body div.math p { text-align: center; } span.eqno { float: right; } /* -- printout stylesheet --------------------------------------------------- */ @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0 !important; width: 100%; } div.sphinxsidebar, div.related, div.footer, #top-link { display: none; } }transaction-1.4.3/docs/_build/html/_static/file.png0000664000175000017500000000061012312641201022144 0ustar tseavertseaverPNG  IHDRabKGD pHYs  tIME  )TIDAT8˭J@Ir('[ "&xYZ X0!i|_@tD] #xjv YNaEi(əy@D&`6PZk$)5%"z.NA#Aba`Vs_3c,2mj [klvy|!Iմy;v "߮a?A7`c^nk?Bg}TЙD# "RD1yER*6MJ3K_Ut8F~IENDB`transaction-1.4.3/docs/_build/html/_static/searchtools.js0000664000175000017500000004270212312641212023415 0ustar tseavertseaver/* * searchtools.js_t * ~~~~~~~~~~~~~~~~ * * Sphinx JavaScript utilties for the full-text search. * * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. * :license: BSD, see LICENSE for details. * */ /** * Porter Stemmer */ var Stemmer = function() { var step2list = { ational: 'ate', tional: 'tion', enci: 'ence', anci: 'ance', izer: 'ize', bli: 'ble', alli: 'al', entli: 'ent', eli: 'e', ousli: 'ous', ization: 'ize', ation: 'ate', ator: 'ate', alism: 'al', iveness: 'ive', fulness: 'ful', ousness: 'ous', aliti: 'al', iviti: 'ive', biliti: 'ble', logi: 'log' }; var step3list = { icate: 'ic', ative: '', alize: 'al', iciti: 'ic', ical: 'ic', ful: '', ness: '' }; var c = "[^aeiou]"; // consonant var v = "[aeiouy]"; // vowel var C = c + "[^aeiouy]*"; // consonant sequence var V = v + "[aeiou]*"; // vowel sequence var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 var s_v = "^(" + C + ")?" + v; // vowel in stem this.stemWord = function (w) { var stem; var suffix; var firstch; var origword = w; if (w.length < 3) return w; var re; var re2; var re3; var re4; firstch = w.substr(0,1); if (firstch == "y") w = firstch.toUpperCase() + w.substr(1); // Step 1a re = /^(.+?)(ss|i)es$/; re2 = /^(.+?)([^s])s$/; if (re.test(w)) w = w.replace(re,"$1$2"); else if (re2.test(w)) w = w.replace(re2,"$1$2"); // Step 1b re = /^(.+?)eed$/; re2 = /^(.+?)(ed|ing)$/; if (re.test(w)) { var fp = re.exec(w); re = new RegExp(mgr0); if (re.test(fp[1])) { re = /.$/; w = w.replace(re,""); } } else if (re2.test(w)) { var fp = re2.exec(w); stem = fp[1]; re2 = new RegExp(s_v); if (re2.test(stem)) { w = stem; re2 = /(at|bl|iz)$/; re3 = new RegExp("([^aeiouylsz])\\1$"); re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); if (re2.test(w)) w = w + "e"; else if (re3.test(w)) { re = /.$/; w = w.replace(re,""); } else if (re4.test(w)) w = w + "e"; } } // Step 1c re = /^(.+?)y$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(s_v); if (re.test(stem)) w = stem + "i"; } // Step 2 re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; suffix = fp[2]; re = new RegExp(mgr0); if (re.test(stem)) w = stem + step2list[suffix]; } // Step 3 re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; suffix = fp[2]; re = new RegExp(mgr0); if (re.test(stem)) w = stem + step3list[suffix]; } // Step 4 re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; re2 = /^(.+?)(s|t)(ion)$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(mgr1); if (re.test(stem)) w = stem; } else if (re2.test(w)) { var fp = re2.exec(w); stem = fp[1] + fp[2]; re2 = new RegExp(mgr1); if (re2.test(stem)) w = stem; } // Step 5 re = /^(.+?)e$/; if (re.test(w)) { var fp = re.exec(w); stem = fp[1]; re = new RegExp(mgr1); re2 = new RegExp(meq1); re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) w = stem; } re = /ll$/; re2 = new RegExp(mgr1); if (re.test(w) && re2.test(w)) { re = /.$/; w = w.replace(re,""); } // and turn initial Y back to y if (firstch == "y") w = firstch.toLowerCase() + w.substr(1); return w; } } /** * Simple result scoring code. */ var Scorer = { // Implement the following function to further tweak the score for each result // The function takes a result array [filename, title, anchor, descr, score] // and returns the new score. /* score: function(result) { return result[4]; }, */ // query matches the full name of an object objNameMatch: 11, // or matches in the last dotted part of the object name objPartialMatch: 6, // Additive scores depending on the priority of the object objPrio: {0: 15, // used to be importantResults 1: 5, // used to be objectResults 2: -5}, // used to be unimportantResults // Used when the priority is not in the mapping. objPrioDefault: 0, // query found in title title: 15, // query found in terms term: 5 }; /** * Search Module */ var Search = { _index : null, _queued_query : null, _pulse_status : -1, init : function() { var params = $.getQueryParameters(); if (params.q) { var query = params.q[0]; $('input[name="q"]')[0].value = query; this.performSearch(query); } }, loadIndex : function(url) { $.ajax({type: "GET", url: url, data: null, dataType: "script", cache: true, complete: function(jqxhr, textstatus) { if (textstatus != "success") { document.getElementById("searchindexloader").src = url; } }}); }, setIndex : function(index) { var q; this._index = index; if ((q = this._queued_query) !== null) { this._queued_query = null; Search.query(q); } }, hasIndex : function() { return this._index !== null; }, deferQuery : function(query) { this._queued_query = query; }, stopPulse : function() { this._pulse_status = 0; }, startPulse : function() { if (this._pulse_status >= 0) return; function pulse() { var i; Search._pulse_status = (Search._pulse_status + 1) % 4; var dotString = ''; for (i = 0; i < Search._pulse_status; i++) dotString += '.'; Search.dots.text(dotString); if (Search._pulse_status > -1) window.setTimeout(pulse, 500); } pulse(); }, /** * perform a search for something (or wait until index is loaded) */ performSearch : function(query) { // create the required interface elements this.out = $('#search-results'); this.title = $('

' + _('Searching') + '

').appendTo(this.out); this.dots = $('').appendTo(this.title); this.status = $('

').appendTo(this.out); this.output = $('