Imported Debian patch 1.0b2-1
Jan Dittberner
12 years ago
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 | ||
0 | 35 | 1.0a4 (2009/1/6) |
1 | 36 | ================ |
2 | 37 |
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 |
0 | 0 | Metadata-Version: 1.0 |
1 | 1 | Name: repoze.tm2 |
2 | Version: 1.0a4 | |
2 | Version: 1.0b2 | |
3 | 3 | Summary: Zope-like transaction manager via WSGI middleware |
4 | 4 | Home-page: http://www.repoze.org |
5 | 5 | Author: Agendaless Consulting |
6 | 6 | Author-email: repoze-dev@lists.repoze.org |
7 | 7 | License: BSD-derived (http://www.repoze.org/LICENSE.txt) |
8 | 8 | Description: repoze.tm2 (Transaction Manager) |
9 | =============================== | |
9 | ================================ | |
10 | 10 | |
11 | 11 | Middleware which uses the ZODB transaction manager to wrap a call to |
12 | 12 | its pipeline children inside a transaction. This is a fork of the |
15 | 15 | |
16 | 16 | See docs/index.rst for documentation. |
17 | 17 | |
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. | |
18 | 53 | |
19 | 54 | 1.0a4 (2009/1/6) |
20 | 55 | ================ |
0 | 0 | repoze.tm2 (Transaction Manager) |
1 | =============================== | |
1 | ================================ | |
2 | 2 | |
3 | 3 | Middleware which uses the ZODB transaction manager to wrap a call to |
4 | 4 | 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 | ||
0 | 23 | python-repoze.tm2 (1.0a4-2) unstable; urgency=low |
1 | 24 | |
2 | 25 | * first upload to unstable |
0 | 0 | Source: python-repoze.tm2 |
1 | 1 | Section: python |
2 | Priority: optional | |
2 | Priority: extra | |
3 | 3 | 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> | |
6 | 5 | Build-Depends: |
7 | debhelper (>= 7), | |
8 | cdbs, | |
9 | python-dev, | |
10 | python-support, | |
6 | debhelper (>= 7.0.50~), | |
7 | python, | |
11 | 8 | python-setuptools, |
12 | python-sphinx, | |
9 | python-sphinx (>= 1.0.7+dfsg), | |
13 | 10 | python-transaction |
14 | Standards-Version: 3.8.2 | |
11 | Standards-Version: 3.9.2 | |
15 | 12 | Homepage: http://www.repoze.org/ |
16 | 13 | Vcs-Svn: svn://svn.debian.org/python-modules/packages/python-repoze.tm2/trunk/ |
17 | 14 | Vcs-Browser: http://svn.debian.org/viewsvn/python-modules/packages/python-repoze.tm2/trunk/ |
21 | 18 | Depends: |
22 | 19 | ${misc:Depends}, |
23 | 20 | ${python:Depends}, |
21 | ${sphinxdoc:Depends}, | |
24 | 22 | python-transaction |
25 | 23 | Description: Zope-like transaction manager via WSGI middleware |
26 | 24 | repoze.tm2 is Python WSGI middleware which uses the ZODB (Zope |
0 | 0 | #!/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 | |
4 | 1 | |
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 | |
8 | 6 | |
9 | build/$(PKG):: | |
7 | override_dh_compress: | |
8 | dh_compress -X .js | |
9 | ||
10 | override_dh_auto_install: | |
10 | 11 | $(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 |
46 | 46 | master_doc = 'index' |
47 | 47 | |
48 | 48 | # 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>' | |
51 | 51 | |
52 | 52 | # The default replacements for |version| and |release|, also used in various |
53 | 53 | # other places throughout the built documents. |
54 | 54 | # |
55 | 55 | # The short X.Y version. |
56 | version = '1.0a4' | |
56 | version = '1.0b2' | |
57 | 57 | # The full version, including alpha/beta/rc tags. |
58 | release = '1.0a4' | |
58 | release = version | |
59 | 59 | |
60 | 60 | # There are two options for replacing |today|: either, you set today to |
61 | 61 | # some non-false value, then it is used: |
77 | 77 | Via ``PasteDeploy`` .INI configuration:: |
78 | 78 | |
79 | 79 | [pipeline:main] |
80 | pipeline = | |
81 | egg:repoze.tm#tm | |
80 | pipeline = | |
81 | egg:repoze.tm2#tm | |
82 | 82 | myapp |
83 | 83 | |
84 | 84 | Via Python: |
89 | 89 | |
90 | 90 | from repoze.tm import TM |
91 | 91 | 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`. | |
92 | 163 | |
93 | 164 | Mocking Up A Data Manager |
94 | 165 | ------------------------- |
297 | 368 | Cleanup |
298 | 369 | ------- |
299 | 370 | |
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: | |
304 | 375 | |
305 | 376 | .. code-block:: python |
306 | 377 | |
307 | 378 | from repoze.tm import isActive |
308 | 379 | tm_active = isActive(wsgi_environment) |
309 | 380 | |
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 | |
313 | 385 | arguments) and a transaction instance: |
314 | 386 | |
315 | 387 | .. code-block:: python |
348 | 420 | |
349 | 421 | The `repoze-dev maillist |
350 | 422 | <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 | ----------- | |
354 | 436 | |
355 | 437 | .. toctree:: |
356 | 438 | :maxdepth: 2 |
357 | 439 | |
358 | 440 | changes |
359 | ||
360 | 441 | |
361 | 442 | Indices and tables |
362 | 443 | ------------------ |
3 | 3 | ekey = 'repoze.tm.active' |
4 | 4 | |
5 | 5 | class TM: |
6 | """ Transaction management WSGI middleware """ | |
6 | 7 | def __init__(self, application, commit_veto=None): |
7 | 8 | self.application = application |
8 | 9 | self.commit_veto = commit_veto |
10 | self.transaction = transaction # for testing | |
9 | 11 | |
10 | 12 | def __call__(self, environ, start_response): |
13 | transaction = self.transaction | |
11 | 14 | environ[ekey] = True |
12 | 15 | transaction.begin() |
13 | 16 | ctx = {} |
17 | ||
14 | 18 | def save_status_and_headers(status, headers, exc_info=None): |
15 | 19 | ctx.update(status=status, headers=headers) |
16 | 20 | return start_response(status, headers, exc_info) |
21 | ||
17 | 22 | try: |
18 | 23 | result = self.application(environ, save_status_and_headers) |
19 | 24 | except: |
20 | 25 | self.abort() |
21 | 26 | 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: | |
25 | 38 | 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() | |
35 | 43 | else: |
36 | 44 | self.commit() |
45 | return result | |
46 | ||
47 | self.commit() | |
37 | 48 | return result |
38 | 49 | |
39 | 50 | def commit(self): |
40 | t = transaction.get() | |
51 | t = self.transaction.get() | |
41 | 52 | t.commit() |
42 | 53 | after_end.cleanup(t) |
43 | 54 | |
44 | 55 | def abort(self): |
45 | t = transaction.get() | |
56 | t = self.transaction.get() | |
46 | 57 | t.abort() |
47 | 58 | after_end.cleanup(t) |
48 | 59 | |
49 | 60 | def isActive(environ): |
61 | """ Return True if the ``repoze.tm.active`` key is in the WSGI | |
62 | environment passed as ``environ``, otherwise return ``False``.""" | |
50 | 63 | if ekey in environ: |
51 | 64 | return True |
52 | 65 | return False |
53 | 66 | |
54 | 67 | # Callback registry API helper class |
55 | 68 | class AfterEnd: |
69 | """ Callback registry API helper class. Use the singleton instance | |
70 | ``repoze.tm.after_end`` when possible.""" | |
56 | 71 | key = '_repoze_tm_afterend' |
57 | 72 | def register(self, func, txn): |
58 | 73 | funcs = getattr(txn, self.key, None) |
84 | 99 | |
85 | 100 | # singleton, importable by other modules |
86 | 101 | 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 | ||
88 | 141 | def make_tm(app, global_conf, commit_veto=None): |
142 | """ Paste filter_app_factory entry point for creation of a TM middleware.""" | |
89 | 143 | from pkg_resources import EntryPoint |
90 | 144 | if commit_veto is not None: |
91 | 145 | commit_veto = EntryPoint.parse('x=%s' % commit_veto).load(False) |
0 | 0 | import unittest |
1 | import sys | |
2 | import transaction | |
3 | 1 | |
4 | 2 | class TestTM(unittest.TestCase): |
5 | 3 | def _getTargetClass(self): |
15 | 13 | def test_ekey_inserted(self): |
16 | 14 | app = DummyApplication() |
17 | 15 | tm = self._makeOne(app) |
16 | tm.transaction = DummyTransactionModule() | |
18 | 17 | from repoze.tm import ekey |
19 | 18 | env = {} |
20 | 19 | tm(env, self._start_response) |
21 | 20 | self.failUnless(ekey in env) |
22 | 21 | |
23 | 22 | 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 | |
27 | 27 | result = tm({}, self._start_response) |
28 | 28 | 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) | |
31 | 31 | |
32 | 32 | 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 | |
36 | 37 | result = tm({}, self._start_response) |
37 | 38 | 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) | |
41 | 41 | |
42 | 42 | 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 | |
46 | 47 | 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) | |
49 | 50 | |
50 | 51 | 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 | |
54 | 56 | 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) | |
57 | 59 | |
58 | 60 | def test_aborted_via_commit_veto(self): |
59 | resource = DummyResource() | |
60 | app = DummyApplication(resource, status="403 Forbidden") | |
61 | app = DummyApplication(status="403 Forbidden") | |
61 | 62 | def commit_veto(environ, status, headers): |
62 | 63 | self.failUnless(isinstance(environ, dict), |
63 | 64 | "environ is not passed properly") |
67 | 68 | "status is not passed properly") |
68 | 69 | return not (200 <= int(status.split()[0]) < 400) |
69 | 70 | tm = self._makeOne(app, commit_veto) |
71 | transaction = DummyTransactionModule() | |
72 | tm.transaction = transaction | |
70 | 73 | 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) | |
73 | 76 | |
74 | 77 | def test_committed_via_commit_veto_exception(self): |
75 | resource = DummyResource() | |
76 | app = DummyApplication(resource, status="403 Forbidden") | |
78 | app = DummyApplication(status="403 Forbidden") | |
77 | 79 | def commit_veto(environ, status, headers): |
78 | 80 | return None |
79 | 81 | tm = self._makeOne(app, commit_veto) |
82 | transaction = DummyTransactionModule() | |
83 | tm.transaction = transaction | |
80 | 84 | 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) | |
83 | 87 | |
84 | 88 | def test_aborted_via_commit_veto_exception(self): |
85 | resource = DummyResource() | |
86 | app = DummyApplication(resource, status="403 Forbidden") | |
89 | app = DummyApplication(status="403 Forbidden") | |
87 | 90 | def commit_veto(environ, status, headers): |
88 | 91 | raise ValueError('foo') |
89 | 92 | tm = self._makeOne(app, commit_veto) |
93 | transaction = DummyTransactionModule() | |
94 | tm.transaction = transaction | |
90 | 95 | 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) | |
93 | 98 | |
94 | 99 | def test_cleanup_on_commit(self): |
100 | from repoze.tm import after_end | |
95 | 101 | dummycalled = [] |
96 | 102 | def dummy(): |
97 | 103 | dummycalled.append(True) |
98 | 104 | 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 | |
103 | 110 | 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) | |
106 | 113 | self.assertEqual(dummycalled, [True]) |
107 | 114 | |
108 | 115 | def test_cleanup_on_abort(self): |
116 | from repoze.tm import after_end | |
109 | 117 | dummycalled = [] |
110 | 118 | def dummy(): |
111 | 119 | dummycalled.append(True) |
112 | 120 | 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 | |
117 | 126 | 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) | |
120 | 129 | self.assertEqual(dummycalled, [True]) |
121 | 130 | |
122 | 131 | class TestAfterEnd(unittest.TestCase): |
130 | 139 | def test_register(self): |
131 | 140 | registry = self._makeOne() |
132 | 141 | func = lambda *x: None |
133 | txn = DummyTransaction() | |
142 | txn = Dummy() | |
134 | 143 | registry.register(func, txn) |
135 | 144 | self.assertEqual(getattr(txn, registry.key), [func]) |
136 | 145 | |
137 | 146 | def test_unregister_exists(self): |
138 | 147 | registry = self._makeOne() |
139 | 148 | func = lambda *x: None |
140 | txn = DummyTransaction() | |
149 | txn = Dummy() | |
141 | 150 | registry.register(func, txn) |
142 | 151 | self.assertEqual(getattr(txn, registry.key), [func]) |
143 | 152 | registry.unregister(func, txn) |
146 | 155 | def test_unregister_notexists(self): |
147 | 156 | registry = self._makeOne() |
148 | 157 | func = lambda *x: None |
149 | txn = DummyTransaction() | |
158 | txn = Dummy() | |
150 | 159 | setattr(txn, registry.key, [None]) |
151 | 160 | registry.unregister(func, txn) |
152 | 161 | 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) | |
153 | 168 | |
154 | 169 | class UtilityFunctionTests(unittest.TestCase): |
155 | 170 | def test_isActive(self): |
169 | 184 | tm = make_tm(DummyApplication(), {}, None) |
170 | 185 | self.assertEqual(tm.commit_veto, None) |
171 | 186 | |
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 | ||
172 | 224 | 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: | |
173 | 250 | pass |
174 | 251 | |
175 | class DummyTransaction: | |
176 | pass | |
177 | ||
178 | 252 | 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"): | |
183 | 254 | self.exception = exception |
184 | self.register = register | |
185 | 255 | self.status = status |
186 | 256 | |
187 | 257 | def __call__(self, environ, start_response): |
188 | 258 | 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() | |
197 | 259 | if self.exception: |
198 | 260 | raise ValueError('raising') |
199 | 261 | return ['hello'] |
200 | 262 | |
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') |
0 | 0 | Metadata-Version: 1.0 |
1 | 1 | Name: repoze.tm2 |
2 | Version: 1.0a4 | |
2 | Version: 1.0b2 | |
3 | 3 | Summary: Zope-like transaction manager via WSGI middleware |
4 | 4 | Home-page: http://www.repoze.org |
5 | 5 | Author: Agendaless Consulting |
6 | 6 | Author-email: repoze-dev@lists.repoze.org |
7 | 7 | License: BSD-derived (http://www.repoze.org/LICENSE.txt) |
8 | 8 | Description: repoze.tm2 (Transaction Manager) |
9 | =============================== | |
9 | ================================ | |
10 | 10 | |
11 | 11 | Middleware which uses the ZODB transaction manager to wrap a call to |
12 | 12 | its pipeline children inside a transaction. This is a fork of the |
15 | 15 | |
16 | 16 | See docs/index.rst for documentation. |
17 | 17 | |
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. | |
18 | 53 | |
19 | 54 | 1.0a4 (2009/1/6) |
20 | 55 | ================ |
0 | .gitignore | |
0 | 1 | CHANGES.txt |
2 | CONTRIBUTORS.txt | |
1 | 3 | COPYRIGHT.txt |
2 | 4 | LICENSE.txt |
3 | 5 | README.txt |
4 | 6 | TODO.txt |
7 | setup.cfg | |
5 | 8 | setup.py |
6 | 9 | docs/Makefile |
10 | docs/api.rst | |
7 | 11 | docs/changes.rst |
8 | 12 | docs/conf.py |
9 | 13 | docs/index.rst |
2 | 2 | tag_date = 0 |
3 | 3 | tag_svn_revision = 0 |
4 | 4 | |
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' | |
1 | 0 | |
2 | 1 | import os |
3 | 2 | from setuptools import setup, find_packages |
7 | 6 | CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() |
8 | 7 | |
9 | 8 | setup(name='repoze.tm2', |
10 | version=__version__, | |
9 | version='1.0b2', | |
11 | 10 | description='Zope-like transaction manager via WSGI middleware', |
12 | 11 | long_description=README + "\n\n" + CHANGES, |
13 | 12 | classifiers=[ |