Skip to content

Commit

Permalink
Fixup with master after latest merges.
Browse files Browse the repository at this point in the history
Also, publish changes on transaction.readthedocs.io, and add more cross-refs.
  • Loading branch information
jamadden committed Dec 11, 2019
1 parent d501419 commit ed097c0
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 101 deletions.
2 changes: 1 addition & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ Bugs fixed:
1.0a1 (2007-12-18)
==================

= Initial release, branched from ZODB trunk on 2007-11-08 (aka
- Initial release, branched from ZODB trunk on 2007-11-08 (aka
"3.9.0dev").

- Remove (deprecated) support for beforeCommitHook alias to
Expand Down
14 changes: 6 additions & 8 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
==================================
:mod:`transaction` API Reference
==================================
===============================
``transaction`` API Reference
===============================

Interfaces
==========
Expand Down Expand Up @@ -43,14 +43,10 @@ Exceptions
API Objects
===========

.. module:: transaction._transaction
.. automodule:: transaction

.. autoclass:: Transaction

.. autoclass:: Savepoint

.. module:: transaction._manager

.. autoclass:: TransactionManager

.. automethod:: __enter__
Expand All @@ -62,3 +58,5 @@ API Objects
On error, aborts the current transaction. Otherwise, commits.

.. autoclass:: ThreadTransactionManager

.. autoclass:: Savepoint
1 change: 1 addition & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.. include:: ../CHANGES.rst
119 changes: 64 additions & 55 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
:mod:`transaction` Documentation
================================
==================================
``transaction`` Documentation
==================================

A general transaction support library for Python.

Expand All @@ -14,6 +15,24 @@ and others. in addition, there are packages like pyramid_tm, which allows all
the code in a web request to run inside of a transaction, and aborts the
transaction automatically if an error occurs. It's also not difficult to create your own backends if necessary.

.. rubric:: Additional Documentation

.. toctree::
:maxdepth: 1

changes
convenience
doom
savepoint
hooks
datamanager
resourcemanager
integrations
sqlalchemy
api
developer


Getting the transaction package
===============================

Expand All @@ -24,6 +43,7 @@ To install the transaction package you can use pip::
After this, the package can be imported in your Python code, but there are a
few things that we need to explain before doing that.


Using transactions
==================

Expand All @@ -46,28 +66,31 @@ example:
transaction.abort()
Things you need to know about the transaction machinery
=======================================================

Transactions
------------
.. rubric:: Transactions


A transaction consists of one or more operations that we want to perform as a
A consists of one or more operations that we want to perform as a
single action. It's an all or nothing proposition: either all the operations
that are part of the transaction are completed successfully or none of them
have any effect.

In the transaction package, a transaction object represents a running
transaction that can be committed or aborted in the end.
In the transaction package, a `transaction object <transaction.interfaces.ITransaction>`
represents a running transaction that can be committed or aborted in
the end.

Transaction managers
--------------------
.. rubric:: Transaction managers

Applications interact with a transaction using a transaction manager, which is
responsible for establishing the transaction boundaries. Basically this means
that it creates the transactions and keeps track of the current one. Whenever
an application wants to use the transaction machinery, it gets the current
transaction from the transaction manager before starting any operations
Applications interact with a transaction using a `transaction manager
<transaction.interfaces.ITransactionManager>`, which is responsible for establishing the
transaction boundaries. Basically this means that it creates the
transactions and keeps track of the current one. Whenever an
application wants to use the transaction machinery, it gets the
current transaction from the transaction manager before starting any
operations

The default transaction manager, `transaction.manager`, is thread
local. You use it as a global variable, but every thread has it's own
Expand All @@ -76,28 +99,29 @@ copy. [#wrapped]_
Application developers will most likely never need to create their own
transaction managers.

Data Managers
-------------
.. rubric:: Data Managers

A data manager handles the interaction between the transaction manager and the
data storage mechanism used by the application, which can be an object storage
like the ZODB, a relational database, a file or any other storage mechanism
A `data manager <transaction.interfaces.IDataManager>` handles the
interaction between the transaction manager and the data storage
mechanism used by the application, which can be an object storage like
the ZODB, a relational database, a file or any other storage mechanism
that the application needs to control.

The data manager provides a common interface for the transaction manager to use
while a transaction is running. To be part of a specific transaction, a data
manager has to 'join' it. Any number of data managers can join a transaction,
which means that you could for example perform writing operations on a ZODB
storage and a relational database as part of the same transaction. The
transaction manager will make sure that both data managers can commit the
transaction or none of them does.
The data manager provides a common interface for the transaction
manager to use while a transaction is running. To be part of a
specific transaction, a data manager has to `join
<transaction.interfaces.ITransaction.join>` it. Any number of data
managers can join a transaction, which means that you could for
example perform writing operations on a ZODB storage and a relational
database as part of the same transaction. The transaction manager will
make sure that both data managers can commit the transaction or none
of them does.

An application developer will need to write a data manager for each different
type of storage that the application uses. There are also third party data
managers that can be used instead.

The two phase commit protocol
-----------------------------
.. rubric:: The two phase commit protocol

The transaction machinery uses a two phase commit protocol for coordinating all
participating data managers in a transaction. The two phases work like follows:
Expand All @@ -115,39 +139,24 @@ participating data managers in a transaction. The two phases work like follows:
The two phase commit sequence requires that all the storages being used are
capable of rolling back or aborting changes.

Savepoints
----------
.. rubric:: Savepoints

A savepoint allows a data manager to save work to its storage without
committing the full transaction. In other words, the transaction will go on,
but if a rollback is needed we can get back to this point instead of starting
all over.
A savepoint allows `supported data managers
<transaction.interfaces.ISavepointDataManager>` to save work to their
storage without committing the full transaction. In other words, the
transaction will go on, but if a rollback is needed we can get back to
this point instead of starting all over.

Savepoints are also useful to free memory that would otherwise be used to keep
the whole state of the transaction. This can be very important when a
transaction attempts a large number of changes.

Additional Documentation
========================

.. toctree::

sqlalchemy
convenience
doom
savepoint
hooks
datamanager
resourcemanager
integrations
api
developer


.. [#wrapped] The thread-local transaction manager,
`transaction.manager` wraps a regular transaction manager. You can
get the wrapped transaction manager using the `manager` attribute.
Implementers of data managers can use this **advanced** feature to
allow graceful shutdown from a central/main thread, by having their
`close` methods call `unregisterSynch` on the wrapped transaction
`transaction.manager` wraps a regular transaction manager. You can
get the wrapped transaction manager using the ``manager``
attribute. Implementers of data managers can use this **advanced**
feature to allow graceful shutdown from a central/main thread, by
having their ``close`` methods call
`~.ITransactionManager.unregisterSynch` on the wrapped transaction
manager they obtained when created or opened.
18 changes: 16 additions & 2 deletions transaction/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,41 @@
# FOR A PARTICULAR PURPOSE.
#
############################################################################
"""Exported transaction functions.
"""``transaction`` module: Exported transaction functions.
$Id$
"""

#: Default implementation of `~ITransaction`
from transaction._transaction import Transaction
#: Default implementation of `~ISavepoint`
from transaction._transaction import Savepoint
#: A single-threaded `~ITransactionManager`
from transaction._manager import TransactionManager
#: A thread-safe `~ITransactionManager`
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.

#: The default transaction manager (a `~.ThreadTransactionManager`). All other functions in
#: this module refer to this object.
manager = ThreadTransactionManager()
#: See `.ITransactionManager.get`
get = __enter__ = manager.get
#: See `.ITransactionManager.begin`
begin = manager.begin
#: See `.ITransactionManager.commit`
commit = manager.commit
#: See `.ITransactionManager.abort`
abort = manager.abort
__exit__ = manager.__exit__
#: See `.ITransactionManager.doom`
doom = manager.doom
#: See `.ITransactionManager.isDoomed`
isDoomed = manager.isDoomed
#: See `.ITransactionManager.savepoint`
savepoint = manager.savepoint
#: See `.ITransactionManager.attempts`
attempts = manager.attempts
31 changes: 17 additions & 14 deletions transaction/_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,17 @@ def _new_transaction(txn, synchs):

@implementer(ITransactionManager)
class TransactionManager(object):
"""
Single-thread implementation of `~transaction.interfaces.ITransactionManager`.
"""

def __init__(self, explicit=False):
self.explicit = explicit
self._txn = None
self._synchs = WeakSet()

def begin(self):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
if self._txn is not None:
if self.explicit:
Expand All @@ -81,7 +84,7 @@ def begin(self):
__enter__ = lambda self: self.begin()

def get(self):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
if self._txn is None:
if self.explicit:
Expand All @@ -95,44 +98,44 @@ def free(self, txn):
self._txn = None

def registerSynch(self, synch):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
self._synchs.add(synch)
if self._txn is not None:
synch.newTransaction(self._txn)

def unregisterSynch(self, synch):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
self._synchs.remove(synch)

def clearSynchs(self):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
self._synchs.clear()

def registeredSynchs(self):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
return bool(self._synchs)

def isDoomed(self):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
return self.get().isDoomed()

def doom(self):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
return self.get().doom()

def commit(self):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
return self.get().commit()

def abort(self):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
return self.get().abort()

Expand All @@ -143,7 +146,7 @@ def __exit__(self, t, v, tb):
self.abort()

def savepoint(self, optimistic=False):
""" See ITransactionManager.
""" See `~transaction.interfaces.ITransactionManager`.
"""
return self.get().savepoint(optimistic)

Expand Down Expand Up @@ -196,7 +199,7 @@ def run(self, func=None, tries=3):
doc = name + u'\n\n' + doc
else:
doc = name

for try_no in itertools.count(1):
txn = self.begin()
if doc:
Expand All @@ -220,13 +223,13 @@ def run(self, func=None, tries=3):
@implementer(ITransactionManager)
class ThreadTransactionManager(threading.local):
"""
Thread-local transaction manager.
Thread-local `transaction manager <transaction.interfaces.ITransactionManager>`.
A thread-local transaction manager can be used as a global
variable, but has a separate copy for each thread.
Advanced applications can use the `manager` attribute to get a
wrapped TransactionManager to allow cross-thread calls for
wrapped `TransactionManager` to allow cross-thread calls for
graceful shutdown of data managers.
"""

Expand Down
Loading

0 comments on commit ed097c0

Please sign in to comment.