Codebase list python-repoze.tm2 / debian/1.0b2-1
Imported Debian patch 1.0b2-1 Jan Dittberner 12 years ago
19 changed file(s) with 586 addition(s) and 144 deletion(s). Raw diff Collapse all Expand all
0 *.pyc
1 dist/
2 *.egg-info
3 .coverage
4 env26/
5 docs/.build/
0 1.0b2 (2011-07-18)
1 ==================
2
3 - A new header ``X-Tm`` is now honored by the ``default_commit_veto`` commit
4 veto hook. If this header exists in the headerlist, its value must be a
5 string. If its value is ``commit``, the transaction will be committed
6 regardless of the status code or the value of ``X-Tm-Abort``. If the value
7 of the ``X-Tm`` header is ``abort`` (or any other string value except
8 ``commit``), the transaction will be aborted regardless of the status code
9 or the value of ``X-Tm-Abort``.
10
11 - Use of the ``X-Tm-Abort`` header is now deprecated. Instead use the
12 ``X-Tm`` header with a value of ``abort`` instead.
13
14 - Add API docs section.
15
16 1.0b1 (2011-01-19)
17 ==================
18
19 - Added ``repoze.tm.default_commit_veto`` commit veto hook. This commit veto
20 hook aborts for 4XX and 5XX response codes, or if there's a header named
21 ``X-Tm-Abort`` in the headerlist and allows a commit otherwise.
22
23 - Documented commit veto hook.
24
25 1.0a5 (2009/9/7)
26 ================
27
28 - Don't commit after aborting if the transaction was doomed or if the
29 commit veto aborted.
30
31 - Don't use "real" transaction module in tests.
32
33 - 100% test coverage.
34
035 1.0a4 (2009/1/6)
136 ================
237
0 Repoze Project Contributor Agreement
1 ====================================
2
3 The submitter agrees by adding his or her name within the section below named
4 "Contributors" and submitting the resulting modified document to the
5 canonical shared repository location for this software project (whether
6 directly, as a user with "direct commit access", or via a "pull request"), he
7 or she is signing a contract electronically. The submitter becomes a
8 Contributor after a) he or she signs this document by adding their name
9 beneath the "Contributors" section below, and b) the resulting document is
10 accepted into the canonical version control repository.
11
12 Treatment of Account
13 ---------------------
14
15 Contributor will not allow anyone other than the Contributor to use his or
16 her username or source repository login to submit code to a Repoze Project
17 source repository. Should Contributor become aware of any such use,
18 Contributor will immediately by notifying Agendaless Consulting.
19 Notification must be performed by sending an email to
20 webmaster@agendaless.com. Until such notice is received, Contributor will be
21 presumed to have taken all actions made through Contributor's account. If the
22 Contributor has direct commit access, Agendaless Consulting will have
23 complete control and discretion over capabilities assigned to Contributor's
24 account, and may disable Contributor's account for any reason at any time.
25
26 Legal Effect of Contribution
27 ----------------------------
28
29 Upon submitting a change or new work to a Repoze Project source Repository (a
30 "Contribution"), you agree to assign, and hereby do assign, a one-half
31 interest of all right, title and interest in and to copyright and other
32 intellectual property rights with respect to your new and original portions
33 of the Contribution to Agendaless Consulting. You and Agendaless Consulting
34 each agree that the other shall be free to exercise any and all exclusive
35 rights in and to the Contribution, without accounting to one another,
36 including without limitation, the right to license the Contribution to others
37 under the Repoze Public License. This agreement shall run with title to the
38 Contribution. Agendaless Consulting does not convey to you any right, title
39 or interest in or to the Program or such portions of the Contribution that
40 were taken from the Program. Your transmission of a submission to the Repoze
41 Project source Repository and marks of identification concerning the
42 Contribution itself constitute your intent to contribute and your assignment
43 of the work in accordance with the provisions of this Agreement.
44
45 License Terms
46 -------------
47
48 Code committed to the Repoze Project source repository (Committed Code) must
49 be governed by the Repoze Public License (http://repoze.org/LICENSE.txt, aka
50 "the RPL") or another license acceptable to Agendaless Consulting. Until
51 Agendaless Consulting declares in writing an acceptable license other than
52 the RPL, only the RPL shall be used. A list of exceptions is detailed within
53 the "Licensing Exceptions" section of this document, if one exists.
54
55 Representations, Warranty, and Indemnification
56 ----------------------------------------------
57
58 Contributor represents and warrants that the Committed Code does not violate
59 the rights of any person or entity, and that the Contributor has legal
60 authority to enter into this Agreement and legal authority over Contributed
61 Code. Further, Contributor indemnifies Agendaless Consulting against
62 violations.
63
64 Cryptography
65 ------------
66
67 Contributor understands that cryptographic code may be subject to government
68 regulations with which Agendaless Consulting and/or entities using Committed
69 Code must comply. Any code which contains any of the items listed below must
70 not be checked-in until Agendaless Consulting staff has been notified and has
71 approved such contribution in writing.
72
73 - Cryptographic capabilities or features
74
75 - Calls to cryptographic features
76
77 - User interface elements which provide context relating to cryptography
78
79 - Code which may, under casual inspection, appear to be cryptographic.
80
81 Notices
82 -------
83
84 Contributor confirms that any notices required will be included in any
85 Committed Code.
86
87 Licensing Exceptions
88 ====================
89
90 None.
91
92 List of Contributors
93 ====================
94
95 The below-signed are contributors to a code repository that is part of the
96 project named "repoze.tm2".
97
98 Each below-signed contributor has read, understand and agrees to the terms
99 above in the section within this document entitled "Repoze Project Contributor
100 Agreement" as of the date beside his or her name.
101
102 Contributors
103 ------------
104
105 - Tres Seaver, 2011/02/22
00 Metadata-Version: 1.0
11 Name: repoze.tm2
2 Version: 1.0a4
2 Version: 1.0b2
33 Summary: Zope-like transaction manager via WSGI middleware
44 Home-page: http://www.repoze.org
55 Author: Agendaless Consulting
66 Author-email: repoze-dev@lists.repoze.org
77 License: BSD-derived (http://www.repoze.org/LICENSE.txt)
88 Description: repoze.tm2 (Transaction Manager)
9 ===============================
9 ================================
1010
1111 Middleware which uses the ZODB transaction manager to wrap a call to
1212 its pipeline children inside a transaction. This is a fork of the
1515
1616 See docs/index.rst for documentation.
1717
18
19 1.0b2 (2011-07-18)
20 ==================
21
22 - A new header ``X-Tm`` is now honored by the ``default_commit_veto`` commit
23 veto hook. If this header exists in the headerlist, its value must be a
24 string. If its value is ``commit``, the transaction will be committed
25 regardless of the status code or the value of ``X-Tm-Abort``. If the value
26 of the ``X-Tm`` header is ``abort`` (or any other string value except
27 ``commit``), the transaction will be aborted regardless of the status code
28 or the value of ``X-Tm-Abort``.
29
30 - Use of the ``X-Tm-Abort`` header is now deprecated. Instead use the
31 ``X-Tm`` header with a value of ``abort`` instead.
32
33 - Add API docs section.
34
35 1.0b1 (2011-01-19)
36 ==================
37
38 - Added ``repoze.tm.default_commit_veto`` commit veto hook. This commit veto
39 hook aborts for 4XX and 5XX response codes, or if there's a header named
40 ``X-Tm-Abort`` in the headerlist and allows a commit otherwise.
41
42 - Documented commit veto hook.
43
44 1.0a5 (2009/9/7)
45 ================
46
47 - Don't commit after aborting if the transaction was doomed or if the
48 commit veto aborted.
49
50 - Don't use "real" transaction module in tests.
51
52 - 100% test coverage.
1853
1954 1.0a4 (2009/1/6)
2055 ================
00 repoze.tm2 (Transaction Manager)
1 ===============================
1 ================================
22
33 Middleware which uses the ZODB transaction manager to wrap a call to
44 its pipeline children inside a transaction. This is a fork of the
0 python-repoze.tm2 (1.0b2-1) unstable; urgency=low
1
2 * New upstream version (Closes: #630704)
3 * Adopting package.
4 * debian/control:
5 - drop cdbs and python-support from Build-Depends
6 - bump required debhelper version to (>= 7.0.50~)
7 - use python (>= 2.6.5~) instead of python-dev in Build-Depends
8 - add version (>= 1.0.7+dfsg) to python-sphinx in Build-Depends
9 - bump Standards-Version to 3.9.2 (no changes needed)
10 - add ${sphinxdoc:Depends} to Depends
11 * debian/rules:
12 - use simplified dh rules using --with python2,sphinxdoc
13 * switch to source format 3.0 (quilt)
14
15 -- Jan Dittberner <jandd@debian.org> Sat, 04 Feb 2012 13:50:37 +0100
16
17 python-repoze.tm2 (1.0a4-3) unstable; urgency=low
18
19 * Orphan package.
20
21 -- Stefano Zacchiroli <zack@debian.org> Wed, 01 Feb 2012 18:20:22 +0100
22
023 python-repoze.tm2 (1.0a4-2) unstable; urgency=low
124
225 * first upload to unstable
00 Source: python-repoze.tm2
11 Section: python
2 Priority: optional
2 Priority: extra
33 Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org>
4 Uploaders:
5 Stefano Zacchiroli <zack@debian.org>
4 Uploaders: Jan Dittberner <jandd@debian.org>
65 Build-Depends:
7 debhelper (>= 7),
8 cdbs,
9 python-dev,
10 python-support,
6 debhelper (>= 7.0.50~),
7 python,
118 python-setuptools,
12 python-sphinx,
9 python-sphinx (>= 1.0.7+dfsg),
1310 python-transaction
14 Standards-Version: 3.8.2
11 Standards-Version: 3.9.2
1512 Homepage: http://www.repoze.org/
1613 Vcs-Svn: svn://svn.debian.org/python-modules/packages/python-repoze.tm2/trunk/
1714 Vcs-Browser: http://svn.debian.org/viewsvn/python-modules/packages/python-repoze.tm2/trunk/
2118 Depends:
2219 ${misc:Depends},
2320 ${python:Depends},
21 ${sphinxdoc:Depends},
2422 python-transaction
2523 Description: Zope-like transaction manager via WSGI middleware
2624 repoze.tm2 is Python WSGI middleware which uses the ZODB (Zope
+0
-1
debian/pycompat less more
0 2
00 #!/usr/bin/make -f
1 DEB_PYTHON_SYSTEM = pysupport
2 include /usr/share/cdbs/1/rules/debhelper.mk
3 include /usr/share/cdbs/1/class/python-distutils.mk
41
5 PKG = python-repoze.tm2
6 DEB_PYTHON_INSTALL_ARGS_ALL += --single-version-externally-managed
7 DEB_COMPRESS_EXCLUDE += .js
2 override_dh_auto_clean:
3 dh_clean
4 find . -type f -name '*.py[co]' -delete
5 $(MAKE) -C docs/ clean
86
9 build/$(PKG)::
7 override_dh_compress:
8 dh_compress -X .js
9
10 override_dh_auto_install:
1011 $(MAKE) -C docs/ html
11 cleanbuilddir/$(PKG)::
12 $(MAKE) -C docs/ clean
12 dh_auto_install
13
14 %:
15 dh --with python2,sphinxdoc --buildsystem=python_distutils $@
0 3.0 (quilt)
0 :mod:`repoze.tm`
1 ----------------
2
3 .. automodule:: repoze.tm
4
5 .. autofunction:: default_commit_veto
6
7 .. autoclass:: TM
8
9 .. autoclass:: AfterEnd
10
11 .. attribute:: after_end
12
13
4646 master_doc = 'index'
4747
4848 # General substitutions.
49 project = 'repoze.tm'
50 copyright = '2008, Repoze Developers <repoze-dev@lists.repoze.org>'
49 project = 'repoze.tm2'
50 copyright = '2008-2011, Repoze Developers <repoze-dev@lists.repoze.org>'
5151
5252 # The default replacements for |version| and |release|, also used in various
5353 # other places throughout the built documents.
5454 #
5555 # The short X.Y version.
56 version = '1.0a4'
56 version = '1.0b2'
5757 # The full version, including alpha/beta/rc tags.
58 release = '1.0a4'
58 release = version
5959
6060 # There are two options for replacing |today|: either, you set today to
6161 # some non-false value, then it is used:
7777 Via ``PasteDeploy`` .INI configuration::
7878
7979 [pipeline:main]
80 pipeline =
81 egg:repoze.tm#tm
80 pipeline =
81 egg:repoze.tm2#tm
8282 myapp
8383
8484 Via Python:
8989
9090 from repoze.tm import TM
9191 new_wsgiapp = TM(mywsgiapp)
92
93 Using A Commit Veto
94 -------------------
95
96 If you'd like to veto commits based on the status code returned by the
97 downstream application, use a commit veto callback.
98
99 First, define the callback somewhere in your application:
100
101 .. code-block:: python
102
103 def commit_veto(environ, status, headers):
104 for header_name, header_value in headers:
105 if header_name.lower() == 'x-tm':
106 if header_value.lower() == 'commit':
107 return False
108 return True
109 for bad in ('4', '5'):
110 if status.startswith(bad):
111 return True
112 return False
113
114 Then configure it into your middleware.
115
116 Via Python:
117
118 .. code-block:: python
119
120 from otherplace import mywsgiapp
121 from my.package import commit_veto
122
123 from repoze.tm import TM
124 new_wsgiapp = TM(mywsgiapp, commit_veto=commit_veto)
125
126 Via PasteDeploy:
127
128 .. code-block:: ini
129
130 [filter:tm]
131 commit_veto = my.package:commit_veto
132
133 In the PasteDeploy example, the path is a Python dotted name, where the dots
134 separate module and package names, and the colon separates a module from its
135 contents. In the above example, the code would be implemented as a
136 "commit_veto" function which lives in the "package" submodule of the "my"
137 package.
138
139 A variant of the commit veto implementation shown above as an example is
140 actually present in the ``repoze.tm2`` package as
141 ``repoze.tm.default_commit_veto``. It's fairly general, so you needn't
142 implement one yourself. Instead just use it.
143
144 Via Python:
145
146 .. code-block:: python
147
148 from otherplace import mywsgiapp
149 from repoze.tm import default_commit_veto
150
151 from repoze.tm import TM
152 new_wsgiapp = TM(mywsgiapp, commit_veto=default_commit_veto)
153
154 Via PasteDeploy:
155
156 .. code-block:: ini
157
158 [filter:tm]
159 commit_veto = repoze.tm:default_commit_veto
160
161 API documentation for ``default_commit_veto`` exists at
162 :func:`pyramid.tm.default_commit_veto`.
92163
93164 Mocking Up A Data Manager
94165 -------------------------
297368 Cleanup
298369 -------
299370
300 When a :mod:`repoze.tm` is in the WSGI pipeline, a boolean key is
301 present in the environment (``repoze.tm.active``). A utility function
302 named isActive can be imported from the :mod:`repoze.tm` package and
303 passed the WSGI environment to check for activation:
371 When a :mod:`repoze.tm` is in the WSGI pipeline, a boolean key is present in
372 the environment (``repoze.tm.active``). A utility function named
373 :func:`pyramid.tm.isActive` can be imported and passed the WSGI environment
374 to check for activation:
304375
305376 .. code-block:: python
306377
307378 from repoze.tm import isActive
308379 tm_active = isActive(wsgi_environment)
309380
310 If an application needs to perform an action after a transaction ends,
311 the "after_end" registry may be used to register a callback. The
312 after_end.register function accepts a callback (accepting no
381 If an application needs to perform an action after a transaction ends, the
382 :attr:`pyramid.tm.after_end` registry may be used to register a callback.
383 This object is an instance fo the :class:`pyramid.tm.AfterEnd` class. The
384 :meth:`pyramid.tm.AfterEnd.register` method accepts a callback (accepting no
313385 arguments) and a transaction instance:
314386
315387 .. code-block:: python
348420
349421 The `repoze-dev maillist
350422 <http://lists.repoze.org/mailman/listinfo/repoze-dev>`_ should be used
351 for communications about this software. Put the overview of the
352 purpose of the package here.
353
423 for communications about this software.
424
425 API Docs
426 ------------------------
427
428 .. toctree::
429 :maxdepth: 3
430
431 api
432
433
434 Change Logs
435 -----------
354436
355437 .. toctree::
356438 :maxdepth: 2
357439
358440 changes
359
360441
361442 Indices and tables
362443 ------------------
33 ekey = 'repoze.tm.active'
44
55 class TM:
6 """ Transaction management WSGI middleware """
67 def __init__(self, application, commit_veto=None):
78 self.application = application
89 self.commit_veto = commit_veto
10 self.transaction = transaction # for testing
911
1012 def __call__(self, environ, start_response):
13 transaction = self.transaction
1114 environ[ekey] = True
1215 transaction.begin()
1316 ctx = {}
17
1418 def save_status_and_headers(status, headers, exc_info=None):
1519 ctx.update(status=status, headers=headers)
1620 return start_response(status, headers, exc_info)
21
1722 try:
1823 result = self.application(environ, save_status_and_headers)
1924 except:
2025 self.abort()
2126 raise
22 else:
23 # ZODB 3.8 + has isDoomed
24 if hasattr(transaction, 'isDoomed') and transaction.isDoomed():
27
28 # ZODB 3.8 + has isDoomed
29 if hasattr(transaction, 'isDoomed') and transaction.isDoomed():
30 self.abort()
31 return result
32
33 if self.commit_veto is not None:
34 try:
35 status, headers = ctx['status'], ctx['headers']
36 veto = self.commit_veto(environ, status, headers)
37 except:
2538 self.abort()
26 if self.commit_veto is not None:
27 try:
28 if self.commit_veto(environ, ctx['status'], ctx['headers']):
29 self.abort()
30 except:
31 self.abort()
32 raise
33 else:
34 self.commit()
39 raise
40
41 if veto:
42 self.abort()
3543 else:
3644 self.commit()
45 return result
46
47 self.commit()
3748 return result
3849
3950 def commit(self):
40 t = transaction.get()
51 t = self.transaction.get()
4152 t.commit()
4253 after_end.cleanup(t)
4354
4455 def abort(self):
45 t = transaction.get()
56 t = self.transaction.get()
4657 t.abort()
4758 after_end.cleanup(t)
4859
4960 def isActive(environ):
61 """ Return True if the ``repoze.tm.active`` key is in the WSGI
62 environment passed as ``environ``, otherwise return ``False``."""
5063 if ekey in environ:
5164 return True
5265 return False
5366
5467 # Callback registry API helper class
5568 class AfterEnd:
69 """ Callback registry API helper class. Use the singleton instance
70 ``repoze.tm.after_end`` when possible."""
5671 key = '_repoze_tm_afterend'
5772 def register(self, func, txn):
5873 funcs = getattr(txn, self.key, None)
8499
85100 # singleton, importable by other modules
86101 after_end = AfterEnd()
87
102
103 def default_commit_veto(environ, status, headers):
104 """
105 When used as a commit veto, the logic in this function will cause the
106 transaction to be committed if:
107
108 - An ``X-Tm`` header with the value ``commit`` exists.
109
110 If an ``X-Tm`` header with the value ``commit`` does not exist, the
111 transaction will be aborted, if:
112
113 - An ``X-Tm`` header with the value ``abort`` (or any value other than
114 ``commit``) exists.
115
116 - An ``X-Tm-Abort`` header exists with any value (for backwards
117 compatibility; prefer ``X-Tm=abort`` in new code).
118
119 - The status code starts with ``4`` or ``5``.
120
121 Otherwise the transaction will be committed by default.
122 """
123 abort_compat = False
124 for header_name, header_value in headers:
125 header_name = header_name.lower()
126 if header_name == 'x-tm':
127 header_value = header_value.lower()
128 if header_value == 'commit':
129 return False
130 return True
131 # x-tm always honored before x-tm-abort 1.0b1 compatibility
132 if header_name == 'x-tm-abort':
133 abort_compat = True
134 if abort_compat:
135 return True
136 for bad in ('4', '5'):
137 if status.startswith(bad):
138 return True
139 return False
140
88141 def make_tm(app, global_conf, commit_veto=None):
142 """ Paste filter_app_factory entry point for creation of a TM middleware."""
89143 from pkg_resources import EntryPoint
90144 if commit_veto is not None:
91145 commit_veto = EntryPoint.parse('x=%s' % commit_veto).load(False)
00 import unittest
1 import sys
2 import transaction
31
42 class TestTM(unittest.TestCase):
53 def _getTargetClass(self):
1513 def test_ekey_inserted(self):
1614 app = DummyApplication()
1715 tm = self._makeOne(app)
16 tm.transaction = DummyTransactionModule()
1817 from repoze.tm import ekey
1918 env = {}
2019 tm(env, self._start_response)
2120 self.failUnless(ekey in env)
2221
2322 def test_committed(self):
24 resource = DummyResource()
25 app = DummyApplication(resource)
26 tm = self._makeOne(app)
23 app = DummyApplication()
24 tm = self._makeOne(app)
25 transaction = DummyTransactionModule()
26 tm.transaction = transaction
2727 result = tm({}, self._start_response)
2828 self.assertEqual(result, ['hello'])
29 self.assertEqual(resource.committed, True)
30 self.assertEqual(resource.aborted, False)
29 self.assertEqual(transaction.committed, True)
30 self.assertEqual(transaction.aborted, False)
3131
3232 def test_aborted_via_doom(self):
33 resource = DummyResource()
34 app = DummyApplication(resource, doom=True)
35 tm = self._makeOne(app)
33 app = DummyApplication()
34 tm = self._makeOne(app)
35 transaction = DummyTransactionModule(doom=True)
36 tm.transaction = transaction
3637 result = tm({}, self._start_response)
3738 self.assertEqual(result, ['hello'])
38 self.assertEqual(transaction.isDoomed(), False)
39 self.assertEqual(resource.committed, False)
40 self.assertEqual(resource.aborted, True)
39 self.assertEqual(transaction.committed, False)
40 self.assertEqual(transaction.aborted, True)
4141
4242 def test_aborted_via_exception(self):
43 resource = DummyResource()
44 app = DummyApplication(resource, exception=True)
45 tm = self._makeOne(app)
43 app = DummyApplication(exception=True)
44 tm = self._makeOne(app)
45 transaction = DummyTransactionModule()
46 tm.transaction = transaction
4647 self.assertRaises(ValueError, tm, {}, self._start_response)
47 self.assertEqual(resource.committed, False)
48 self.assertEqual(resource.aborted, True)
48 self.assertEqual(transaction.committed, False)
49 self.assertEqual(transaction.aborted, True)
4950
5051 def test_aborted_via_exception_and_doom(self):
51 resource = DummyResource()
52 app = DummyApplication(resource, exception=True, doom=True)
53 tm = self._makeOne(app)
52 app = DummyApplication(exception=True)
53 tm = self._makeOne(app)
54 transaction = DummyTransactionModule(doom=True)
55 tm.transaction = transaction
5456 self.assertRaises(ValueError, tm, {}, self._start_response)
55 self.assertEqual(resource.committed, False)
56 self.assertEqual(resource.aborted, True)
57 self.assertEqual(transaction.committed, False)
58 self.assertEqual(transaction.aborted, True)
5759
5860 def test_aborted_via_commit_veto(self):
59 resource = DummyResource()
60 app = DummyApplication(resource, status="403 Forbidden")
61 app = DummyApplication(status="403 Forbidden")
6162 def commit_veto(environ, status, headers):
6263 self.failUnless(isinstance(environ, dict),
6364 "environ is not passed properly")
6768 "status is not passed properly")
6869 return not (200 <= int(status.split()[0]) < 400)
6970 tm = self._makeOne(app, commit_veto)
71 transaction = DummyTransactionModule()
72 tm.transaction = transaction
7073 tm({}, self._start_response)
71 self.assertEqual(resource.committed, False)
72 self.assertEqual(resource.aborted, True)
74 self.assertEqual(transaction.committed, False)
75 self.assertEqual(transaction.aborted, True)
7376
7477 def test_committed_via_commit_veto_exception(self):
75 resource = DummyResource()
76 app = DummyApplication(resource, status="403 Forbidden")
78 app = DummyApplication(status="403 Forbidden")
7779 def commit_veto(environ, status, headers):
7880 return None
7981 tm = self._makeOne(app, commit_veto)
82 transaction = DummyTransactionModule()
83 tm.transaction = transaction
8084 tm({}, self._start_response)
81 self.assertEqual(resource.committed, True)
82 self.assertEqual(resource.aborted, False)
85 self.assertEqual(transaction.committed, True)
86 self.assertEqual(transaction.aborted, False)
8387
8488 def test_aborted_via_commit_veto_exception(self):
85 resource = DummyResource()
86 app = DummyApplication(resource, status="403 Forbidden")
89 app = DummyApplication(status="403 Forbidden")
8790 def commit_veto(environ, status, headers):
8891 raise ValueError('foo')
8992 tm = self._makeOne(app, commit_veto)
93 transaction = DummyTransactionModule()
94 tm.transaction = transaction
9095 self.assertRaises(ValueError, tm, {}, self._start_response)
91 self.assertEqual(resource.committed, False)
92 self.assertEqual(resource.aborted, True)
96 self.assertEqual(transaction.committed, False)
97 self.assertEqual(transaction.aborted, True)
9398
9499 def test_cleanup_on_commit(self):
100 from repoze.tm import after_end
95101 dummycalled = []
96102 def dummy():
97103 dummycalled.append(True)
98104 env = {}
99 resource = DummyResource()
100 app = DummyApplication(resource, exception=False, doom=False,
101 register=dummy)
102 tm = self._makeOne(app)
105 app = DummyApplication()
106 tm = self._makeOne(app)
107 transaction = DummyTransactionModule()
108 setattr(transaction, after_end.key, [dummy])
109 tm.transaction = transaction
103110 tm(env, self._start_response)
104 self.assertEqual(resource.committed, True)
105 self.assertEqual(resource.aborted, False)
111 self.assertEqual(transaction.committed, True)
112 self.assertEqual(transaction.aborted, False)
106113 self.assertEqual(dummycalled, [True])
107114
108115 def test_cleanup_on_abort(self):
116 from repoze.tm import after_end
109117 dummycalled = []
110118 def dummy():
111119 dummycalled.append(True)
112120 env = {}
113 resource = DummyResource()
114 app = DummyApplication(resource, exception=True, doom=False,
115 register=dummy)
116 tm = self._makeOne(app)
121 app = DummyApplication(exception=True)
122 tm = self._makeOne(app)
123 transaction = DummyTransactionModule()
124 setattr(transaction, after_end.key, [dummy])
125 tm.transaction = transaction
117126 self.assertRaises(ValueError, tm, env, self._start_response)
118 self.assertEqual(resource.committed, False)
119 self.assertEqual(resource.aborted, True)
127 self.assertEqual(transaction.committed, False)
128 self.assertEqual(transaction.aborted, True)
120129 self.assertEqual(dummycalled, [True])
121130
122131 class TestAfterEnd(unittest.TestCase):
130139 def test_register(self):
131140 registry = self._makeOne()
132141 func = lambda *x: None
133 txn = DummyTransaction()
142 txn = Dummy()
134143 registry.register(func, txn)
135144 self.assertEqual(getattr(txn, registry.key), [func])
136145
137146 def test_unregister_exists(self):
138147 registry = self._makeOne()
139148 func = lambda *x: None
140 txn = DummyTransaction()
149 txn = Dummy()
141150 registry.register(func, txn)
142151 self.assertEqual(getattr(txn, registry.key), [func])
143152 registry.unregister(func, txn)
146155 def test_unregister_notexists(self):
147156 registry = self._makeOne()
148157 func = lambda *x: None
149 txn = DummyTransaction()
158 txn = Dummy()
150159 setattr(txn, registry.key, [None])
151160 registry.unregister(func, txn)
152161 self.assertEqual(getattr(txn, registry.key), [None])
162
163 def test_unregister_funcs_is_None(self):
164 registry = self._makeOne()
165 func = lambda *x: None
166 txn = Dummy()
167 self.assertEqual(registry.unregister(func, txn), None)
153168
154169 class UtilityFunctionTests(unittest.TestCase):
155170 def test_isActive(self):
169184 tm = make_tm(DummyApplication(), {}, None)
170185 self.assertEqual(tm.commit_veto, None)
171186
187 class Test_default_commit_veto(unittest.TestCase):
188 def _callFUT(self, status, headers=()):
189 from repoze.tm import default_commit_veto
190 return default_commit_veto(None, status, headers)
191
192 def test_it_true_5XX(self):
193 self.failUnless(self._callFUT('500 Server Error'))
194 self.failUnless(self._callFUT('503 Service Unavailable'))
195
196 def test_it_true_4XX(self):
197 self.failUnless(self._callFUT('400 Bad Request'))
198 self.failUnless(self._callFUT('411 Length Required'))
199
200 def test_it_false_2XX(self):
201 self.failIf(self._callFUT('200 OK'))
202 self.failIf(self._callFUT('201 Created'))
203
204 def test_it_false_3XX(self):
205 self.failIf(self._callFUT('301 Moved Permanently'))
206 self.failIf(self._callFUT('302 Found'))
207
208 def test_it_true_x_tm_abort_specific(self):
209 self.failUnless(self._callFUT('200 OK', [('X-Tm-Abort', True)]))
210
211 def test_it_false_x_tm_commit(self):
212 self.failIf(self._callFUT('200 OK', [('X-Tm', 'commit')]))
213
214 def test_it_true_x_tm_abort(self):
215 self.failUnless(self._callFUT('200 OK', [('X-Tm', 'abort')]))
216
217 def test_it_true_x_tm_anythingelse(self):
218 self.failUnless(self._callFUT('200 OK', [('X-Tm', '')]))
219
220 def test_x_tm_generic_precedes_x_tm_abort_specific(self):
221 self.failIf(self._callFUT('200 OK', [('X-Tm', 'commit'),
222 ('X-Tm-Abort', True)]))
223
172224 def fakeveto(environ, status, headers):
225 """ """
226
227 class DummyTransactionModule:
228 begun = False
229 committed = False
230 aborted = False
231 def __init__(self, doom=False):
232 self.doom = doom
233
234 def begin(self):
235 self.begun = True
236
237 def get(self):
238 return self
239
240 def commit(self):
241 self.committed = True
242
243 def abort(self):
244 self.aborted = True
245
246 def isDoomed(self):
247 return self.doom
248
249 class Dummy:
173250 pass
174251
175 class DummyTransaction:
176 pass
177
178252 class DummyApplication:
179 def __init__(self, resource=None, doom=False, exception=False,
180 register=None, status="200 OK"):
181 self.resource = resource
182 self.doom = doom
253 def __init__(self, exception=False, status="200 OK"):
183254 self.exception = exception
184 self.register = register
185255 self.status = status
186256
187257 def __call__(self, environ, start_response):
188258 start_response(self.status, [], None)
189 t = transaction.get()
190 if self.resource:
191 t.join(self.resource)
192 if self.register:
193 from repoze.tm import after_end
194 after_end.register(self.register, t)
195 if self.doom:
196 t.doom()
197259 if self.exception:
198260 raise ValueError('raising')
199261 return ['hello']
200262
201 class DummyResource:
202 committed = False
203 aborted = False
204
205 def sortKey(self):
206 return 1
207
208 tpc_finish = tpc_abort = tpc_vote = tpc_begin = lambda *arg: None
209
210 def commit(self, txn):
211 self.committed = True
212
213 def abort(self, txn):
214 self.aborted = True
215
216 def test_suite():
217 return unittest.findTestCases(sys.modules[__name__])
218
219 if __name__ == '__main__':
220 unittest.main(defaultTest='test_suite')
00 Metadata-Version: 1.0
11 Name: repoze.tm2
2 Version: 1.0a4
2 Version: 1.0b2
33 Summary: Zope-like transaction manager via WSGI middleware
44 Home-page: http://www.repoze.org
55 Author: Agendaless Consulting
66 Author-email: repoze-dev@lists.repoze.org
77 License: BSD-derived (http://www.repoze.org/LICENSE.txt)
88 Description: repoze.tm2 (Transaction Manager)
9 ===============================
9 ================================
1010
1111 Middleware which uses the ZODB transaction manager to wrap a call to
1212 its pipeline children inside a transaction. This is a fork of the
1515
1616 See docs/index.rst for documentation.
1717
18
19 1.0b2 (2011-07-18)
20 ==================
21
22 - A new header ``X-Tm`` is now honored by the ``default_commit_veto`` commit
23 veto hook. If this header exists in the headerlist, its value must be a
24 string. If its value is ``commit``, the transaction will be committed
25 regardless of the status code or the value of ``X-Tm-Abort``. If the value
26 of the ``X-Tm`` header is ``abort`` (or any other string value except
27 ``commit``), the transaction will be aborted regardless of the status code
28 or the value of ``X-Tm-Abort``.
29
30 - Use of the ``X-Tm-Abort`` header is now deprecated. Instead use the
31 ``X-Tm`` header with a value of ``abort`` instead.
32
33 - Add API docs section.
34
35 1.0b1 (2011-01-19)
36 ==================
37
38 - Added ``repoze.tm.default_commit_veto`` commit veto hook. This commit veto
39 hook aborts for 4XX and 5XX response codes, or if there's a header named
40 ``X-Tm-Abort`` in the headerlist and allows a commit otherwise.
41
42 - Documented commit veto hook.
43
44 1.0a5 (2009/9/7)
45 ================
46
47 - Don't commit after aborting if the transaction was doomed or if the
48 commit veto aborted.
49
50 - Don't use "real" transaction module in tests.
51
52 - 100% test coverage.
1853
1954 1.0a4 (2009/1/6)
2055 ================
0 .gitignore
01 CHANGES.txt
2 CONTRIBUTORS.txt
13 COPYRIGHT.txt
24 LICENSE.txt
35 README.txt
46 TODO.txt
7 setup.cfg
58 setup.py
69 docs/Makefile
10 docs/api.rst
711 docs/changes.rst
812 docs/conf.py
913 docs/index.rst
22 tag_date = 0
33 tag_svn_revision = 0
44
5 [nosetests]
6 cover-package = repoze.tm
7 nocapture = 1
8 cover-erase = 1
9 where = repoze/tm
10 match = ^test
11
0 __version__ = '1.0a4'
10
21 import os
32 from setuptools import setup, find_packages
76 CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
87
98 setup(name='repoze.tm2',
10 version=__version__,
9 version='1.0b2',
1110 description='Zope-like transaction manager via WSGI middleware',
1211 long_description=README + "\n\n" + CHANGES,
1312 classifiers=[