Commit 2df3cf17 authored by Tres Seaver's avatar Tres Seaver

Move whole-file doctests into docs/.

parent af2c1b2b
......@@ -10,6 +10,8 @@ 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())
......@@ -17,6 +19,8 @@ We can now use the with statement to define transaction boundaries.
We can use it with a manager:
.. doctest::
>>> with transaction.manager as t:
... dm['z'] = 3
... t.note('test 3')
......@@ -46,7 +50,9 @@ 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::
overcome transient failures. This typically looks something like:
.. doctest::
for i in range(3):
try:
......@@ -62,6 +68,7 @@ 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:
......@@ -96,6 +103,8 @@ 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
......@@ -110,6 +119,8 @@ propagated.
Of course, other errors are propagated directly:
.. doctest::
>>> ntry = 0
>>> for attempt in transaction.manager.attempts():
... with attempt:
......@@ -122,6 +133,8 @@ Of course, other errors are propagated directly:
We can use the default transaction manager:
.. doctest::
>>> for attempt in transaction.attempts():
... with attempt as t:
... t.note('test')
......@@ -140,6 +153,8 @@ 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):
......
......@@ -25,6 +25,8 @@ 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)
......@@ -45,6 +47,8 @@ To see how it works we first need to create a stub data manager:
Start a new transaction:
.. doctest::
>>> import transaction
>>> txn = transaction.begin()
>>> dm = DataManager()
......@@ -56,27 +60,37 @@ 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
......@@ -86,15 +100,21 @@ DoomedTransaction:
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']
......@@ -103,6 +123,8 @@ Which aborts the data manager:
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
......@@ -115,6 +137,8 @@ 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()
......@@ -125,6 +149,8 @@ after it's been committed is probably a programming error:
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()
......@@ -132,5 +158,7 @@ be able to join it:
Clean up:
.. doctest::
>>> txn = transaction.begin()
>>> txn.abort()
......@@ -6,6 +6,9 @@ Contents:
.. toctree::
:maxdepth: 2
convenience
doom
savepoint
api
......
......@@ -24,6 +24,8 @@ 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()
......@@ -31,12 +33,16 @@ storage of named immutable values, like strings and numbers.
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'
......@@ -52,6 +58,8 @@ 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
......@@ -60,6 +68,8 @@ some accounts:
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)
......@@ -67,6 +77,8 @@ Now, we'll define a validation function to validate an account:
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:
......@@ -86,6 +98,8 @@ way, it rolls back all of its changes and prints the error:
Now let's try applying some entries:
.. doctest::
>>> apply_entries([
... ('bob', 10.0),
... ('sally', 10.0),
......@@ -109,6 +123,8 @@ Now let's try applying some entries:
If we provide entries that cause an unexpected error:
.. doctest::
>>> apply_entries([
... ('bob', 10.0),
... ('sally', 10.0),
......@@ -123,6 +139,8 @@ 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
......@@ -132,6 +150,8 @@ previous call to ``apply_entries``:
If we now abort the outer transactions, the earlier changes will go
away:
.. doctest::
>>> transaction.abort()
>>> dm['bob-balance']
......@@ -145,6 +165,8 @@ Savepoint invalidation
A savepoint can be used any number of times:
.. doctest::
>>> dm['bob-balance'] = 100.0
>>> dm['bob-balance']
100.0
......@@ -170,6 +192,8 @@ A savepoint can be used any number of times:
However, using a savepoint invalidates any savepoints that come after it:
.. doctest::
>>> dm['bob-balance'] = 200.0
>>> dm['bob-balance']
200.0
......@@ -203,6 +227,8 @@ 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()
......@@ -219,6 +245,8 @@ 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'
......@@ -245,6 +273,8 @@ 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):
...
......@@ -255,11 +285,15 @@ savepoint. If we try to commit the transaction, the commit will fail:
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
......@@ -280,6 +314,8 @@ data manager that didn't support savepoints:
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()
......
......@@ -16,7 +16,7 @@
Sample data manager implementation that illustrates how to implement
savepoints.
See savepoint.txt in the transaction package.
Used by savepoint.rst in the Sphinx docs.
"""
from zope.interface import implementer
......
......@@ -80,7 +80,6 @@ the dm would end up being joined twice, leading to extra tpc calls and pain.
def test_suite():
return unittest.TestSuite((
doctest.DocFileSuite('savepoint.txt'),
doctest.DocTestSuite(),
))
......
......@@ -36,10 +36,8 @@ TODO
add in tests for objects which are modified multiple times,
for example an object that gets modified in multiple sub txns.
"""
from doctest import DocTestSuite, DocFileSuite, IGNORE_EXCEPTION_DETAIL
from doctest import DocTestSuite
import struct
import sys
import unittest
import transaction
......@@ -763,14 +761,10 @@ def bug239086():
def test_suite():
suite = unittest.TestSuite((
DocFileSuite('doom.txt'),
DocTestSuite(),
unittest.makeSuite(TransactionTests),
unittest.makeSuite(Test_oid_repr),
))
if sys.version_info >= (2, 6):
suite.addTest(DocFileSuite('convenience.txt',
optionflags=IGNORE_EXCEPTION_DETAIL))
return suite
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment