Imported Upstream version 1.0a4
SVN-Git Migration
8 years ago
0 | 1.0a4 (2009/1/6) | |
1 | ================ | |
2 | ||
3 | - RESTify CHANGES, move docs in README.txt into Sphinx. | |
4 | ||
5 | - Remove ``setup.cfg`` (all dependencies available via PyPI). | |
6 | ||
7 | - Synchronization point with ``repoze.tm`` (0.9). | |
8 | ||
9 | 1.0a3 | |
10 | ===== | |
11 | ||
12 | Allow ``commit_veto`` hook to be specified within Paste config, ala:: | |
13 | ||
14 | [filter:tm] | |
15 | use = repoze.tm:make_tm | |
16 | commit_veto = some.package:myfunction | |
17 | ||
18 | ``myfunction`` should take three args: environ, status, headers and | |
19 | should return True if the txn should be aborted, False if it should be | |
20 | committed. | |
21 | ||
22 | Initial PyPI release. | |
23 | ||
24 | 1.0a2 (2008/7/15) | |
25 | ================= | |
26 | ||
27 | - Provide "commit_veto" hook point (contributed by Alberto Valverde). | |
28 | ||
29 | - Point easy_install at http://dist.repoze.org/tm2/dev/simple via setup.cfg. | |
30 | ||
31 | 1.0a1 | |
32 | ===== | |
33 | ||
34 | - Fork point: we've created repoze.tm2, which is repoze.tm that has a | |
35 | dependency only on the 'transaction' package instead of all of ZODB. | |
36 | ||
37 | - Better documentation for non-Zope usage in README.txt. | |
38 | ||
39 | 0.8 | |
40 | === | |
41 | ||
42 | - Relaxed requirement for ZODB 3.7.2, since we might need to use the | |
43 | package with other verions. Note that the tests which depend on | |
44 | transaction having "doom" semantics don't work with 3.7.2, anyway. | |
45 | ||
46 | 0.7 | |
47 | === | |
48 | ||
49 | - Depend on PyPI release of ZODB 3.7.2. Upgrade to this by doing | |
50 | bin/easy_install -U 'ZODB3 >= 3.7.1, < 3.8.0a' if necessary. | |
51 | ||
52 | 0.6 | |
53 | === | |
54 | ||
55 | - after_end.register and after_end.unregister must now be passed a | |
56 | transaction object rather than a WSGI environment to avoid the | |
57 | possibility that the WSGI environment used by a child participating | |
58 | in transaction management won't be the same one used by the | |
59 | repoze.tm package. | |
60 | ||
61 | - repoze.tm now inserts a key into the WSGI environment | |
62 | (``repoze.tm.active``) if it's active in the WSGI pipeline. An API | |
63 | function, repoze.tm:isActive can be called with a single argument, | |
64 | the WSGI environment, to check if the middleware is active. | |
65 | ||
66 | 0.5 | |
67 | === | |
68 | ||
69 | - Depend on rerolled ZODB 3.7.1 instead of zopelib. | |
70 | ||
71 | - Add license and copyright, change trove classifiers. | |
72 | ||
73 | 0.4 | |
74 | === | |
75 | ||
76 | - Depend on zopelib rather than ZODB 3.8.0b3 distribution, because the | |
77 | ZODB distribution pulls in various packages (zope.interface and ZEO | |
78 | most notably) that are incompatible with stock Zope 2.10.4 apps and | |
79 | older sandboxes. We'll need to revisit this. | |
80 | ||
81 | 0.3 | |
82 | === | |
83 | ||
84 | - Provide limited compatibility for older transaction package versions | |
85 | which don't support the 'transaction.isDoomed' API. | |
86 | ||
87 | 0.2 | |
88 | === | |
89 | ||
90 | - Provide after_end API for registering callbacks at transaction end. | |
91 | ||
92 | 0.1 | |
93 | === | |
94 | ||
95 | - Initial Release |
0 | Copyright (c) 2007 Agendaless Consulting and Contributors. | |
1 | (http://www.agendaless.com), All Rights Reserved | |
2 |
0 | License | |
1 | ||
2 | A copyright notice accompanies this license document that identifies | |
3 | the copyright holders. | |
4 | ||
5 | Redistribution and use in source and binary forms, with or without | |
6 | modification, are permitted provided that the following conditions are | |
7 | met: | |
8 | ||
9 | 1. Redistributions in source code must retain the accompanying | |
10 | copyright notice, this list of conditions, and the following | |
11 | disclaimer. | |
12 | ||
13 | 2. Redistributions in binary form must reproduce the accompanying | |
14 | copyright notice, this list of conditions, and the following | |
15 | disclaimer in the documentation and/or other materials provided | |
16 | with the distribution. | |
17 | ||
18 | 3. Names of the copyright holders must not be used to endorse or | |
19 | promote products derived from this software without prior | |
20 | written permission from the copyright holders. | |
21 | ||
22 | 4. If any files are modified, you must cause the modified files to | |
23 | carry prominent notices stating that you changed the files and | |
24 | the date of any change. | |
25 | ||
26 | Disclaimer | |
27 | ||
28 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND | |
29 | ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | |
30 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | |
31 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
32 | HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
33 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | |
34 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
35 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
36 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |
37 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF | |
38 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
39 | SUCH DAMAGE. | |
40 |
0 | Metadata-Version: 1.0 | |
1 | Name: repoze.tm2 | |
2 | Version: 1.0a4 | |
3 | Summary: Zope-like transaction manager via WSGI middleware | |
4 | Home-page: http://www.repoze.org | |
5 | Author: Agendaless Consulting | |
6 | Author-email: repoze-dev@lists.repoze.org | |
7 | License: BSD-derived (http://www.repoze.org/LICENSE.txt) | |
8 | Description: repoze.tm2 (Transaction Manager) | |
9 | =============================== | |
10 | ||
11 | Middleware which uses the ZODB transaction manager to wrap a call to | |
12 | its pipeline children inside a transaction. This is a fork of the | |
13 | ``repoze.tm`` package which depends only on the ``transaction`` | |
14 | package rather than the entirety of ZODB (for users who don't rely on ZODB). | |
15 | ||
16 | See docs/index.rst for documentation. | |
17 | ||
18 | ||
19 | 1.0a4 (2009/1/6) | |
20 | ================ | |
21 | ||
22 | - RESTify CHANGES, move docs in README.txt into Sphinx. | |
23 | ||
24 | - Remove ``setup.cfg`` (all dependencies available via PyPI). | |
25 | ||
26 | - Synchronization point with ``repoze.tm`` (0.9). | |
27 | ||
28 | 1.0a3 | |
29 | ===== | |
30 | ||
31 | Allow ``commit_veto`` hook to be specified within Paste config, ala:: | |
32 | ||
33 | [filter:tm] | |
34 | use = repoze.tm:make_tm | |
35 | commit_veto = some.package:myfunction | |
36 | ||
37 | ``myfunction`` should take three args: environ, status, headers and | |
38 | should return True if the txn should be aborted, False if it should be | |
39 | committed. | |
40 | ||
41 | Initial PyPI release. | |
42 | ||
43 | 1.0a2 (2008/7/15) | |
44 | ================= | |
45 | ||
46 | - Provide "commit_veto" hook point (contributed by Alberto Valverde). | |
47 | ||
48 | - Point easy_install at http://dist.repoze.org/tm2/dev/simple via setup.cfg. | |
49 | ||
50 | 1.0a1 | |
51 | ===== | |
52 | ||
53 | - Fork point: we've created repoze.tm2, which is repoze.tm that has a | |
54 | dependency only on the 'transaction' package instead of all of ZODB. | |
55 | ||
56 | - Better documentation for non-Zope usage in README.txt. | |
57 | ||
58 | 0.8 | |
59 | === | |
60 | ||
61 | - Relaxed requirement for ZODB 3.7.2, since we might need to use the | |
62 | package with other verions. Note that the tests which depend on | |
63 | transaction having "doom" semantics don't work with 3.7.2, anyway. | |
64 | ||
65 | 0.7 | |
66 | === | |
67 | ||
68 | - Depend on PyPI release of ZODB 3.7.2. Upgrade to this by doing | |
69 | bin/easy_install -U 'ZODB3 >= 3.7.1, < 3.8.0a' if necessary. | |
70 | ||
71 | 0.6 | |
72 | === | |
73 | ||
74 | - after_end.register and after_end.unregister must now be passed a | |
75 | transaction object rather than a WSGI environment to avoid the | |
76 | possibility that the WSGI environment used by a child participating | |
77 | in transaction management won't be the same one used by the | |
78 | repoze.tm package. | |
79 | ||
80 | - repoze.tm now inserts a key into the WSGI environment | |
81 | (``repoze.tm.active``) if it's active in the WSGI pipeline. An API | |
82 | function, repoze.tm:isActive can be called with a single argument, | |
83 | the WSGI environment, to check if the middleware is active. | |
84 | ||
85 | 0.5 | |
86 | === | |
87 | ||
88 | - Depend on rerolled ZODB 3.7.1 instead of zopelib. | |
89 | ||
90 | - Add license and copyright, change trove classifiers. | |
91 | ||
92 | 0.4 | |
93 | === | |
94 | ||
95 | - Depend on zopelib rather than ZODB 3.8.0b3 distribution, because the | |
96 | ZODB distribution pulls in various packages (zope.interface and ZEO | |
97 | most notably) that are incompatible with stock Zope 2.10.4 apps and | |
98 | older sandboxes. We'll need to revisit this. | |
99 | ||
100 | 0.3 | |
101 | === | |
102 | ||
103 | - Provide limited compatibility for older transaction package versions | |
104 | which don't support the 'transaction.isDoomed' API. | |
105 | ||
106 | 0.2 | |
107 | === | |
108 | ||
109 | - Provide after_end API for registering callbacks at transaction end. | |
110 | ||
111 | 0.1 | |
112 | === | |
113 | ||
114 | - Initial Release | |
115 | ||
116 | Keywords: web application server wsgi zope repoze | |
117 | Platform: UNKNOWN | |
118 | Classifier: Development Status :: 3 - Alpha | |
119 | Classifier: Intended Audience :: Developers | |
120 | Classifier: Programming Language :: Python | |
121 | Classifier: Topic :: Internet :: WWW/HTTP | |
122 | Classifier: Topic :: Internet :: WWW/HTTP :: WSGI | |
123 | Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware |
0 | repoze.tm2 (Transaction Manager) | |
1 | =============================== | |
2 | ||
3 | Middleware which uses the ZODB transaction manager to wrap a call to | |
4 | its pipeline children inside a transaction. This is a fork of the | |
5 | ``repoze.tm`` package which depends only on the ``transaction`` | |
6 | package rather than the entirety of ZODB (for users who don't rely on ZODB). | |
7 | ||
8 | See docs/index.rst for documentation. |
0 | Investigate using the absurd API for transaction synchronizers e.g.: | |
1 | ||
2 | class RepozeTMSync: | |
3 | def beforeCompletion(self, transaction): | |
4 | for thing in self.before: | |
5 | thing() | |
6 | ||
7 | def afterCompletion(self, transaction): | |
8 | for thing in self.after: | |
9 | thing() |
Binary diff not shown
0 | @import url('default.css'); | |
1 | body { | |
2 | background-color: #006339; | |
3 | } | |
4 | ||
5 | div.document { | |
6 | background-color: #dad3bd; | |
7 | } | |
8 | ||
9 | div.sphinxsidebar h3,h4,h5,li,a { | |
10 | color: #127c56 !important; | |
11 | } | |
12 | ||
13 | div.related { | |
14 | color: #dad3bd; | |
15 | background-color: #00744a; | |
16 | } | |
17 | ||
18 | div.related a { | |
19 | color: #dad3bd; | |
20 | } |
0 | # Makefile for Sphinx documentation | |
1 | # | |
2 | ||
3 | # You can set these variables from the command line. | |
4 | SPHINXOPTS = | |
5 | SPHINXBUILD = sphinx-build | |
6 | PAPER = | |
7 | ||
8 | # Internal variables. | |
9 | PAPEROPT_a4 = -D latex_paper_size=a4 | |
10 | PAPEROPT_letter = -D latex_paper_size=letter | |
11 | ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . | |
12 | ||
13 | .PHONY: help clean html web pickle htmlhelp latex changes linkcheck | |
14 | ||
15 | help: | |
16 | @echo "Please use \`make <target>' where <target> is one of" | |
17 | @echo " html to make standalone HTML files" | |
18 | @echo " pickle to make pickle files (usable by e.g. sphinx-web)" | |
19 | @echo " htmlhelp to make HTML files and a HTML help project" | |
20 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" | |
21 | @echo " changes to make an overview over all changed/added/deprecated items" | |
22 | @echo " linkcheck to check all external links for integrity" | |
23 | ||
24 | clean: | |
25 | -rm -rf .build/* | |
26 | ||
27 | html: | |
28 | mkdir -p .build/html .build/doctrees | |
29 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html | |
30 | @echo | |
31 | @echo "Build finished. The HTML pages are in .build/html." | |
32 | ||
33 | pickle: | |
34 | mkdir -p .build/pickle .build/doctrees | |
35 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle | |
36 | @echo | |
37 | @echo "Build finished; now you can process the pickle files or run" | |
38 | @echo " sphinx-web .build/pickle" | |
39 | @echo "to start the sphinx-web server." | |
40 | ||
41 | web: pickle | |
42 | ||
43 | htmlhelp: | |
44 | mkdir -p .build/htmlhelp .build/doctrees | |
45 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp | |
46 | @echo | |
47 | @echo "Build finished; now you can run HTML Help Workshop with the" \ | |
48 | ".hhp project file in .build/htmlhelp." | |
49 | ||
50 | latex: | |
51 | mkdir -p .build/latex .build/doctrees | |
52 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex | |
53 | @echo | |
54 | @echo "Build finished; the LaTeX files are in .build/latex." | |
55 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ | |
56 | "run these through (pdf)latex." | |
57 | ||
58 | changes: | |
59 | mkdir -p .build/changes .build/doctrees | |
60 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes | |
61 | @echo | |
62 | @echo "The overview file is in .build/changes." | |
63 | ||
64 | linkcheck: | |
65 | mkdir -p .build/linkcheck .build/doctrees | |
66 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck | |
67 | @echo | |
68 | @echo "Link check complete; look for any errors in the above output " \ | |
69 | "or in .build/linkcheck/output.txt." |
0 | # -*- coding: utf-8 -*- | |
1 | # | |
2 | # repoze.tm documentation build configuration file | |
3 | # | |
4 | # This file is execfile()d with the current directory set to its containing | |
5 | # dir. | |
6 | # | |
7 | # The contents of this file are pickled, so don't put values in the | |
8 | # namespace that aren't pickleable (module imports are okay, they're | |
9 | # removed automatically). | |
10 | # | |
11 | # All configuration values have a default value; values that are commented | |
12 | # out serve to show the default value. | |
13 | ||
14 | import sys, os | |
15 | ||
16 | # If your extensions are in another directory, add it here. If the | |
17 | # directory is relative to the documentation root, use os.path.abspath to | |
18 | # make it absolute, like shown here. | |
19 | #sys.path.append(os.path.abspath('some/directory')) | |
20 | ||
21 | parent = os.path.dirname(os.path.dirname(__file__)) | |
22 | sys.path.append(os.path.abspath(parent)) | |
23 | wd = os.getcwd() | |
24 | os.chdir(parent) | |
25 | os.system('%s setup.py test -q' % sys.executable) | |
26 | os.chdir(wd) | |
27 | ||
28 | for item in os.listdir(parent): | |
29 | if item.endswith('.egg'): | |
30 | sys.path.append(os.path.join(parent, item)) | |
31 | ||
32 | # General configuration | |
33 | # --------------------- | |
34 | ||
35 | # Add any Sphinx extension module names here, as strings. They can be | |
36 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. | |
37 | extensions = ['sphinx.ext.autodoc'] | |
38 | ||
39 | # Add any paths that contain templates here, relative to this directory. | |
40 | templates_path = ['.templates'] | |
41 | ||
42 | # The suffix of source filenames. | |
43 | source_suffix = '.rst' | |
44 | ||
45 | # The master toctree document. | |
46 | master_doc = 'index' | |
47 | ||
48 | # General substitutions. | |
49 | project = 'repoze.tm' | |
50 | copyright = '2008, Repoze Developers <repoze-dev@lists.repoze.org>' | |
51 | ||
52 | # The default replacements for |version| and |release|, also used in various | |
53 | # other places throughout the built documents. | |
54 | # | |
55 | # The short X.Y version. | |
56 | version = '1.0a4' | |
57 | # The full version, including alpha/beta/rc tags. | |
58 | release = '1.0a4' | |
59 | ||
60 | # There are two options for replacing |today|: either, you set today to | |
61 | # some non-false value, then it is used: | |
62 | #today = '' | |
63 | # Else, today_fmt is used as the format for a strftime call. | |
64 | today_fmt = '%B %d, %Y' | |
65 | ||
66 | # List of documents that shouldn't be included in the build. | |
67 | #unused_docs = [] | |
68 | ||
69 | # List of directories, relative to source directories, that shouldn't be | |
70 | # searched for source files. | |
71 | #exclude_dirs = [] | |
72 | ||
73 | # The reST default role (used for this markup: `text`) to use for all | |
74 | # documents. | |
75 | #default_role = None | |
76 | ||
77 | # If true, '()' will be appended to :func: etc. cross-reference text. | |
78 | #add_function_parentheses = True | |
79 | ||
80 | # If true, the current module name will be prepended to all description | |
81 | # unit titles (such as .. function::). | |
82 | #add_module_names = True | |
83 | ||
84 | # If true, sectionauthor and moduleauthor directives will be shown in the | |
85 | # output. They are ignored by default. | |
86 | #show_authors = False | |
87 | ||
88 | # The name of the Pygments (syntax highlighting) style to use. | |
89 | pygments_style = 'sphinx' | |
90 | ||
91 | ||
92 | # Options for HTML output | |
93 | # ----------------------- | |
94 | ||
95 | # The style sheet to use for HTML and HTML Help pages. A file of that name | |
96 | # must exist either in Sphinx' static/ path, or in one of the custom paths | |
97 | # given in html_static_path. | |
98 | html_style = 'repoze.css' | |
99 | ||
100 | # The name for this set of Sphinx documents. If None, it defaults to | |
101 | # "<project> v<release> documentation". | |
102 | #html_title = None | |
103 | ||
104 | # A shorter title for the navigation bar. Default is the same as | |
105 | # html_title. | |
106 | #html_short_title = None | |
107 | ||
108 | # The name of an image file (within the static path) to place at the top of | |
109 | # the sidebar. | |
110 | html_logo = '.static/logo_hi.gif' | |
111 | ||
112 | # The name of an image file (within the static path) to use as favicon of | |
113 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or | |
114 | # 32x32 pixels large. | |
115 | #html_favicon = None | |
116 | ||
117 | # Add any paths that contain custom static files (such as style sheets) | |
118 | # here, relative to this directory. They are copied after the builtin | |
119 | # static files, so a file named "default.css" will overwrite the builtin | |
120 | # "default.css". | |
121 | html_static_path = ['.static'] | |
122 | ||
123 | # If not '', a 'Last updated on:' timestamp is inserted at every page | |
124 | # bottom, using the given strftime format. | |
125 | html_last_updated_fmt = '%b %d, %Y' | |
126 | ||
127 | # If true, SmartyPants will be used to convert quotes and dashes to | |
128 | # typographically correct entities. | |
129 | #html_use_smartypants = True | |
130 | ||
131 | # Custom sidebar templates, maps document names to template names. | |
132 | #html_sidebars = {} | |
133 | ||
134 | # Additional templates that should be rendered to pages, maps page names to | |
135 | # template names. | |
136 | #html_additional_pages = {} | |
137 | ||
138 | # If false, no module index is generated. | |
139 | #html_use_modindex = True | |
140 | ||
141 | # If false, no index is generated. | |
142 | #html_use_index = True | |
143 | ||
144 | # If true, the index is split into individual pages for each letter. | |
145 | #html_split_index = False | |
146 | ||
147 | # If true, the reST sources are included in the HTML build as | |
148 | # _sources/<name>. | |
149 | #html_copy_source = True | |
150 | ||
151 | # If true, an OpenSearch description file will be output, and all pages | |
152 | # will contain a <link> tag referring to it. The value of this option must | |
153 | # be the base URL from which the finished HTML is served. | |
154 | #html_use_opensearch = '' | |
155 | ||
156 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). | |
157 | #html_file_suffix = '' | |
158 | ||
159 | # Output file base name for HTML help builder. | |
160 | htmlhelp_basename = 'tmdoc' | |
161 | ||
162 | ||
163 | # Options for LaTeX output | |
164 | # ------------------------ | |
165 | ||
166 | # The paper size ('letter' or 'a4'). | |
167 | #latex_paper_size = 'letter' | |
168 | ||
169 | # The font size ('10pt', '11pt' or '12pt'). | |
170 | #latex_font_size = '10pt' | |
171 | ||
172 | # Grouping the document tree into LaTeX files. List of tuples | |
173 | # (source start file, target name, title, | |
174 | # author, document class [howto/manual]). | |
175 | latex_documents = [ | |
176 | ('index', 'tm.tex', 'repoze.tm Documentation', | |
177 | 'Repoze Developers', 'manual'), | |
178 | ] | |
179 | ||
180 | # The name of an image file (relative to this directory) to place at the | |
181 | # top of the title page. | |
182 | latex_logo = '.static/logo_hi.gif' | |
183 | ||
184 | # For "manual" documents, if this is true, then toplevel headings are | |
185 | # parts, not chapters. | |
186 | #latex_use_parts = False | |
187 | ||
188 | # Additional stuff for the LaTeX preamble. | |
189 | #latex_preamble = '' | |
190 | ||
191 | # Documents to append as an appendix to all manuals. | |
192 | #latex_appendices = [] | |
193 | ||
194 | # If false, no module index is generated. | |
195 | #latex_use_modindex = True |
0 | Documentation for repoze.tm2 (``repoze.tm`` fork) | |
1 | ================================================= | |
2 | ||
3 | Overview | |
4 | -------- | |
5 | ||
6 | :mod:`repoze.tm2` is WSGI middleware which uses the ``ZODB`` package's | |
7 | transaction manager to wrap a call to its pipeline children inside a | |
8 | transaction. | |
9 | ||
10 | .. note:: :mod:`repoze.tm2` is equivalent to the :mod:`repoze.tm` | |
11 | package (it was forked from :mod:`repoze.tm`), except it has a | |
12 | dependency only on the ``transaction`` package rather than a | |
13 | dependency on the entire ``ZODB3`` package (``ZODB3`` 3.8 ships | |
14 | with the ``transaction`` package right now). It is an error to | |
15 | install both repoze.tm and repoze.tm2 into the same environment, as | |
16 | they provide the same entry points and import points. | |
17 | ||
18 | Behavior | |
19 | -------- | |
20 | ||
21 | When this middleware is present in the WSGI pipeline, a new | |
22 | transaction will be started once a WSGI request makes it to the | |
23 | :mod:`repoze.tm` middleware. If any downstream application raises an | |
24 | exception, the transaction will be aborted, otherwise the transaction | |
25 | will be committed. Any "data managers" participating in the | |
26 | transaction will be aborted or committed respectively. A ZODB | |
27 | "connection" is an example of a data manager. | |
28 | ||
29 | Since this is a tiny wrapper around the ZODB transaction module, and | |
30 | the ZODB transaction module is "thread-safe" (in the sense that its | |
31 | default policy is to create a new transaction for each thread), it | |
32 | should be fine to use in either multiprocess or multithread | |
33 | environments. | |
34 | ||
35 | Purpose and Usage | |
36 | ----------------- | |
37 | ||
38 | The ZODB transaction manager is a completely generic transaction | |
39 | manager. It can be used independently of the actual "object database" | |
40 | part of ZODB. One of the purposes of creating :mod:`repoze.tm` was to | |
41 | allow for systems other than Zope to make use of two-phase commit | |
42 | transactions in a WSGI context. | |
43 | ||
44 | Let's pretend we have an existing system that places data into a | |
45 | relational database when someone submits a form. The system has been | |
46 | running for a while, and our code handles the database commit and | |
47 | rollback for us explicitly; if the form processing succeeds, our code | |
48 | commits the database transaction. If it fails, our code rolls back | |
49 | the database transaction. Everything works fine. | |
50 | ||
51 | Now our customer asks us if we can also place data into another | |
52 | separate relational database when the form is submitted as well as | |
53 | continuing to place data in the original database. We need to put | |
54 | data in both databases, and if we want to ensure that no records exist | |
55 | in one that don't exist in the other as a result of a form submission, | |
56 | we're going to need to do a pretty complicated commit and rollback | |
57 | dance in each place in our code which needs to write to both data | |
58 | stores. We can't just blindly commit one, then commit the other, | |
59 | because the second commit may fail and we'll be left with "orphan" | |
60 | data in the first, and we'll either need to clean it up manually or | |
61 | leave it there to trip over later. | |
62 | ||
63 | A transaction manager helps us ensure that no data is committed to | |
64 | either database unless both participating data stores can commit. | |
65 | Once the transaction manager determines that both data stores are | |
66 | willing to commit, it will commit them both in very quick succession, | |
67 | so that there is only a minimal chance that the second data store will | |
68 | fail to commit. If it does, the system will raise an error that makes | |
69 | it impossible to begin another transaction until the system restarts, | |
70 | so the damage is minimized. In practice, this error almost never | |
71 | occurs unless the code that interfaces the database to the transaction | |
72 | manager has a bug. | |
73 | ||
74 | Adding :mod:`repoze.tm` To Your WSGI Pipeline | |
75 | --------------------------------------------- | |
76 | ||
77 | Via ``PasteDeploy`` .INI configuration:: | |
78 | ||
79 | [pipeline:main] | |
80 | pipeline = | |
81 | egg:repoze.tm#tm | |
82 | myapp | |
83 | ||
84 | Via Python: | |
85 | ||
86 | .. code-block:: python | |
87 | ||
88 | from otherplace import mywsgiapp | |
89 | ||
90 | from repoze.tm import TM | |
91 | new_wsgiapp = TM(mywsgiapp) | |
92 | ||
93 | Mocking Up A Data Manager | |
94 | ------------------------- | |
95 | ||
96 | The piece of code you need to write in order to participate in ZODB | |
97 | transactions is called a 'data manager'. It is typically a class. | |
98 | Here's the interface that you need to implement in the code for a data | |
99 | manager: | |
100 | ||
101 | .. code-block:: python | |
102 | ||
103 | class IDataManager(zope.interface.Interface): | |
104 | """Objects that manage transactional storage. | |
105 | ||
106 | These objects may manage data for other objects, or they | |
107 | may manage non-object storages, such as relational | |
108 | databases. For example, a ZODB.Connection. | |
109 | ||
110 | Note that when some data is modified, that data's data | |
111 | manager should join a transaction so that data can be | |
112 | committed when the user commits the transaction. """ | |
113 | ||
114 | transaction_manager = zope.interface.Attribute( | |
115 | """The transaction manager (TM) used by this data | |
116 | manager. | |
117 | ||
118 | This is a public attribute, intended for read-only | |
119 | use. The value is an instance of ITransactionManager, | |
120 | typically set by the data manager's constructor. """ | |
121 | ) | |
122 | ||
123 | def abort(transaction): | |
124 | """Abort a transaction and forget all changes. | |
125 | ||
126 | Abort must be called outside of a two-phase commit. | |
127 | ||
128 | Abort is called by the transaction manager to abort transactions | |
129 | that are not yet in a two-phase commit. | |
130 | """ | |
131 | ||
132 | # Two-phase commit protocol. These methods are called by | |
133 | # the ITransaction object associated with the transaction | |
134 | # being committed. The sequence of calls normally follows | |
135 | # this regular expression: tpc_begin commit tpc_vote | |
136 | # (tpc_finish | tpc_abort) | |
137 | ||
138 | def tpc_begin(transaction): | |
139 | ||
140 | """Begin commit of a transaction, starting the | |
141 | two-phase commit. | |
142 | ||
143 | transaction is the ITransaction instance associated with the | |
144 | transaction being committed. | |
145 | """ | |
146 | ||
147 | def commit(transaction): | |
148 | ||
149 | """Commit modifications to registered objects. | |
150 | ||
151 | Save changes to be made persistent if the transaction | |
152 | commits (if tpc_finish is called later). If tpc_abort | |
153 | is called later, changes must not persist. | |
154 | ||
155 | This includes conflict detection and handling. If no | |
156 | conflicts or errors occur, the data manager should be | |
157 | prepared to make the changes persist when tpc_finish | |
158 | is called. """ | |
159 | ||
160 | def tpc_vote(transaction): | |
161 | """Verify that a data manager can commit the transaction. | |
162 | ||
163 | This is the last chance for a data manager to vote 'no'. A | |
164 | data manager votes 'no' by raising an exception. | |
165 | ||
166 | transaction is the ITransaction instance associated with the | |
167 | transaction being committed. | |
168 | """ | |
169 | ||
170 | def tpc_finish(transaction): | |
171 | ||
172 | """Indicate confirmation that the transaction is done. | |
173 | ||
174 | Make all changes to objects modified by this | |
175 | transaction persist. | |
176 | ||
177 | transaction is the ITransaction instance associated | |
178 | with the transaction being committed. | |
179 | ||
180 | This should never fail. If this raises an exception, | |
181 | the database is not expected to maintain consistency; | |
182 | it's a serious error. """ | |
183 | ||
184 | def tpc_abort(transaction): | |
185 | ||
186 | """Abort a transaction. | |
187 | ||
188 | This is called by a transaction manager to end a | |
189 | two-phase commit on the data manager. Abandon all | |
190 | changes to objects modified by this transaction. | |
191 | ||
192 | transaction is the ITransaction instance associated | |
193 | with the transaction being committed. | |
194 | ||
195 | This should never fail. | |
196 | """ | |
197 | ||
198 | def sortKey(): | |
199 | ||
200 | """Return a key to use for ordering registered | |
201 | DataManagers. | |
202 | ||
203 | ZODB uses a global sort order to prevent deadlock when | |
204 | it commits transactions involving multiple resource | |
205 | managers. The resource manager must define a | |
206 | sortKey() method that provides a global ordering for | |
207 | resource managers. """ | |
208 | # Alternate version: | |
209 | #"""Return a consistent sort key for this connection. | |
210 | # #This allows ordering multiple connections that use | |
211 | the same storage in #a consistent manner. This is | |
212 | unique for the lifetime of a connection, #which is | |
213 | good enough to avoid ZEO deadlocks. #""" | |
214 | ||
215 | Let's implement a mock data manager. Our mock data manager will write | |
216 | data to a file if the transaction commits. It will not write data to | |
217 | a file if the transaction aborts: | |
218 | ||
219 | .. code-block:: python | |
220 | ||
221 | class MockDataManager: | |
222 | ||
223 | transaction_manager = None | |
224 | ||
225 | def __init__(self, data, path): | |
226 | self.data = data | |
227 | self.path = path | |
228 | ||
229 | def abort(self, transaction): | |
230 | pass | |
231 | ||
232 | def tpc_begin(self, transaction): | |
233 | pass | |
234 | ||
235 | def commit(self, transaction): | |
236 | import tempfile | |
237 | self.tempfn = tempfile.mktemp() | |
238 | temp = open(self.tempfn, 'wb') | |
239 | temp.write(self.data) | |
240 | temp.flush() | |
241 | temp.close() | |
242 | ||
243 | def tpc_vote(self, transaction): | |
244 | import os | |
245 | if not os.path.exists(self.tempfn): | |
246 | raise ValueError('%s doesnt exist' % self.tempfn) | |
247 | if os.path.exists(self.path): | |
248 | raise ValueError('file already exists') | |
249 | ||
250 | def tpc_finish(self, transaction): | |
251 | import os | |
252 | os.rename(self.tempfn, self.path) | |
253 | ||
254 | def tpc_abort(self, transaction): | |
255 | import os | |
256 | try: | |
257 | os.remove(self.tempfn) | |
258 | except OSError: | |
259 | pass | |
260 | ||
261 | We can create a datamanager and join it into the currently running | |
262 | transaction: | |
263 | ||
264 | .. code-block:: python | |
265 | ||
266 | dm = MockDataManager('heres the data', '/tmp/file') | |
267 | import transaction | |
268 | t = transaction.get() | |
269 | t.join(dm) | |
270 | ||
271 | When the transaction commits, a file will be placed in '/tmp/file' | |
272 | containing 'heres the data'. If the transaction aborts, no file will | |
273 | be created. | |
274 | ||
275 | If more than one data manager is joined to the transaction, all of | |
276 | them must be willing to commit or the entire transaction is aborted | |
277 | and none of them commit. If you can imagine creating two of the mock | |
278 | data managers we've made within application code, if one has a problem | |
279 | during "tpc_vote", neither will actually write a file to the ultimate | |
280 | location, and thus your application consistency is maintained. | |
281 | ||
282 | Integrating Your Data Manager With :mod:`repoze.tm` | |
283 | --------------------------------------------------- | |
284 | ||
285 | The :mod:`repoze.tm` transaction management machinery has an implicit | |
286 | policy. When it is in the WSGI pipeline, a transaction is started | |
287 | when the middleware is invoked. Thus, in your application code, | |
288 | calling "import transaction; transaction.get()" will return the | |
289 | transaction object created by the :mod:`repoze.tm` middleware. You | |
290 | needn't call t.commit() or t.abort() within your application code. | |
291 | You only need to call t.join, to register your data manager with the | |
292 | transaction. :mod:`repoze.tm` will abort the transaction if an | |
293 | exception is raised by your application code or lower middleware | |
294 | before it returns a WSGI response. If your application or lower | |
295 | middleware raises an exception, the transaction is aborted. | |
296 | ||
297 | Cleanup | |
298 | ------- | |
299 | ||
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: | |
304 | ||
305 | .. code-block:: python | |
306 | ||
307 | from repoze.tm import isActive | |
308 | tm_active = isActive(wsgi_environment) | |
309 | ||
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 | |
313 | arguments) and a transaction instance: | |
314 | ||
315 | .. code-block:: python | |
316 | ||
317 | from repoze.tm import after_end | |
318 | import transaction | |
319 | t = transaction.get() # the current transaction | |
320 | def func(): | |
321 | pass # close a connection, etc | |
322 | after_end.register(func, t) | |
323 | ||
324 | "after_end" callbacks should only be registered when the transaction | |
325 | manager is active, or a memory leak will result (registration cleanup | |
326 | happens only on transaction commit or abort, which is managed by | |
327 | :mod:`repoze.tm` while in the pipeline). | |
328 | ||
329 | Further Documentation | |
330 | --------------------- | |
331 | ||
332 | Many database adapters written for Zope (e.g. for Postgres, MySQL, | |
333 | etc) use this transaction manager, so it should be possible to take a | |
334 | look in these places to see how to implement a more real-world | |
335 | transaction-aware database connector that uses this module in non-Zope | |
336 | applications: | |
337 | ||
338 | - http://svn.zope.org/ZODB/branches/3.7/src/transaction/ | |
339 | ||
340 | - http://svn.zope.org/ZODB/branches/3.8/src/transaction/ | |
341 | ||
342 | - http://mysql-python.sourceforge.net/ (ZMySQLDA) | |
343 | ||
344 | - http://www.initd.org/svn/psycopg/psycopg2/trunk/ (ZPsycoPGDA) | |
345 | ||
346 | Contacting | |
347 | ---------- | |
348 | ||
349 | The `repoze-dev maillist | |
350 | <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 | ||
354 | ||
355 | .. toctree:: | |
356 | :maxdepth: 2 | |
357 | ||
358 | changes | |
359 | ||
360 | ||
361 | Indices and tables | |
362 | ------------------ | |
363 | ||
364 | * :ref:`genindex` | |
365 | * :ref:`modindex` | |
366 | * :ref:`search` |
0 | # repoze TransactionManager WSGI middleware | |
1 | import transaction | |
2 | ||
3 | ekey = 'repoze.tm.active' | |
4 | ||
5 | class TM: | |
6 | def __init__(self, application, commit_veto=None): | |
7 | self.application = application | |
8 | self.commit_veto = commit_veto | |
9 | ||
10 | def __call__(self, environ, start_response): | |
11 | environ[ekey] = True | |
12 | transaction.begin() | |
13 | ctx = {} | |
14 | def save_status_and_headers(status, headers, exc_info=None): | |
15 | ctx.update(status=status, headers=headers) | |
16 | return start_response(status, headers, exc_info) | |
17 | try: | |
18 | result = self.application(environ, save_status_and_headers) | |
19 | except: | |
20 | self.abort() | |
21 | raise | |
22 | else: | |
23 | # ZODB 3.8 + has isDoomed | |
24 | if hasattr(transaction, 'isDoomed') and transaction.isDoomed(): | |
25 | 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() | |
35 | else: | |
36 | self.commit() | |
37 | return result | |
38 | ||
39 | def commit(self): | |
40 | t = transaction.get() | |
41 | t.commit() | |
42 | after_end.cleanup(t) | |
43 | ||
44 | def abort(self): | |
45 | t = transaction.get() | |
46 | t.abort() | |
47 | after_end.cleanup(t) | |
48 | ||
49 | def isActive(environ): | |
50 | if ekey in environ: | |
51 | return True | |
52 | return False | |
53 | ||
54 | # Callback registry API helper class | |
55 | class AfterEnd: | |
56 | key = '_repoze_tm_afterend' | |
57 | def register(self, func, txn): | |
58 | funcs = getattr(txn, self.key, None) | |
59 | if funcs is None: | |
60 | funcs = [] | |
61 | setattr(txn, self.key, funcs) | |
62 | funcs.append(func) | |
63 | ||
64 | def unregister(self, func, txn): | |
65 | funcs = getattr(txn, self.key, None) | |
66 | if funcs is None: | |
67 | return | |
68 | new = [] | |
69 | for f in funcs: | |
70 | if f is func: | |
71 | continue | |
72 | new.append(f) | |
73 | if new: | |
74 | setattr(txn, self.key, new) | |
75 | else: | |
76 | delattr(txn, self.key) | |
77 | ||
78 | def cleanup(self, txn): | |
79 | funcs = getattr(txn, self.key, None) | |
80 | if funcs is not None: | |
81 | for func in funcs: | |
82 | func() | |
83 | delattr(txn, self.key) | |
84 | ||
85 | # singleton, importable by other modules | |
86 | after_end = AfterEnd() | |
87 | ||
88 | def make_tm(app, global_conf, commit_veto=None): | |
89 | from pkg_resources import EntryPoint | |
90 | if commit_veto is not None: | |
91 | commit_veto = EntryPoint.parse('x=%s' % commit_veto).load(False) | |
92 | return TM(app, commit_veto) | |
93 |
0 | import unittest | |
1 | import sys | |
2 | import transaction | |
3 | ||
4 | class TestTM(unittest.TestCase): | |
5 | def _getTargetClass(self): | |
6 | from repoze.tm import TM | |
7 | return TM | |
8 | ||
9 | def _start_response(self, status, headers, exc_info=None): | |
10 | pass | |
11 | ||
12 | def _makeOne(self, app, commit_veto=None): | |
13 | return self._getTargetClass()(app, commit_veto) | |
14 | ||
15 | def test_ekey_inserted(self): | |
16 | app = DummyApplication() | |
17 | tm = self._makeOne(app) | |
18 | from repoze.tm import ekey | |
19 | env = {} | |
20 | tm(env, self._start_response) | |
21 | self.failUnless(ekey in env) | |
22 | ||
23 | def test_committed(self): | |
24 | resource = DummyResource() | |
25 | app = DummyApplication(resource) | |
26 | tm = self._makeOne(app) | |
27 | result = tm({}, self._start_response) | |
28 | self.assertEqual(result, ['hello']) | |
29 | self.assertEqual(resource.committed, True) | |
30 | self.assertEqual(resource.aborted, False) | |
31 | ||
32 | def test_aborted_via_doom(self): | |
33 | resource = DummyResource() | |
34 | app = DummyApplication(resource, doom=True) | |
35 | tm = self._makeOne(app) | |
36 | result = tm({}, self._start_response) | |
37 | self.assertEqual(result, ['hello']) | |
38 | self.assertEqual(transaction.isDoomed(), False) | |
39 | self.assertEqual(resource.committed, False) | |
40 | self.assertEqual(resource.aborted, True) | |
41 | ||
42 | def test_aborted_via_exception(self): | |
43 | resource = DummyResource() | |
44 | app = DummyApplication(resource, exception=True) | |
45 | tm = self._makeOne(app) | |
46 | self.assertRaises(ValueError, tm, {}, self._start_response) | |
47 | self.assertEqual(resource.committed, False) | |
48 | self.assertEqual(resource.aborted, True) | |
49 | ||
50 | def test_aborted_via_exception_and_doom(self): | |
51 | resource = DummyResource() | |
52 | app = DummyApplication(resource, exception=True, doom=True) | |
53 | tm = self._makeOne(app) | |
54 | self.assertRaises(ValueError, tm, {}, self._start_response) | |
55 | self.assertEqual(resource.committed, False) | |
56 | self.assertEqual(resource.aborted, True) | |
57 | ||
58 | def test_aborted_via_commit_veto(self): | |
59 | resource = DummyResource() | |
60 | app = DummyApplication(resource, status="403 Forbidden") | |
61 | def commit_veto(environ, status, headers): | |
62 | self.failUnless(isinstance(environ, dict), | |
63 | "environ is not passed properly") | |
64 | self.failUnless(isinstance(headers, list), | |
65 | "headers are not passed properly") | |
66 | self.failUnless(isinstance(status, str), | |
67 | "status is not passed properly") | |
68 | return not (200 <= int(status.split()[0]) < 400) | |
69 | tm = self._makeOne(app, commit_veto) | |
70 | tm({}, self._start_response) | |
71 | self.assertEqual(resource.committed, False) | |
72 | self.assertEqual(resource.aborted, True) | |
73 | ||
74 | def test_committed_via_commit_veto_exception(self): | |
75 | resource = DummyResource() | |
76 | app = DummyApplication(resource, status="403 Forbidden") | |
77 | def commit_veto(environ, status, headers): | |
78 | return None | |
79 | tm = self._makeOne(app, commit_veto) | |
80 | tm({}, self._start_response) | |
81 | self.assertEqual(resource.committed, True) | |
82 | self.assertEqual(resource.aborted, False) | |
83 | ||
84 | def test_aborted_via_commit_veto_exception(self): | |
85 | resource = DummyResource() | |
86 | app = DummyApplication(resource, status="403 Forbidden") | |
87 | def commit_veto(environ, status, headers): | |
88 | raise ValueError('foo') | |
89 | tm = self._makeOne(app, commit_veto) | |
90 | self.assertRaises(ValueError, tm, {}, self._start_response) | |
91 | self.assertEqual(resource.committed, False) | |
92 | self.assertEqual(resource.aborted, True) | |
93 | ||
94 | def test_cleanup_on_commit(self): | |
95 | dummycalled = [] | |
96 | def dummy(): | |
97 | dummycalled.append(True) | |
98 | env = {} | |
99 | resource = DummyResource() | |
100 | app = DummyApplication(resource, exception=False, doom=False, | |
101 | register=dummy) | |
102 | tm = self._makeOne(app) | |
103 | tm(env, self._start_response) | |
104 | self.assertEqual(resource.committed, True) | |
105 | self.assertEqual(resource.aborted, False) | |
106 | self.assertEqual(dummycalled, [True]) | |
107 | ||
108 | def test_cleanup_on_abort(self): | |
109 | dummycalled = [] | |
110 | def dummy(): | |
111 | dummycalled.append(True) | |
112 | env = {} | |
113 | resource = DummyResource() | |
114 | app = DummyApplication(resource, exception=True, doom=False, | |
115 | register=dummy) | |
116 | tm = self._makeOne(app) | |
117 | self.assertRaises(ValueError, tm, env, self._start_response) | |
118 | self.assertEqual(resource.committed, False) | |
119 | self.assertEqual(resource.aborted, True) | |
120 | self.assertEqual(dummycalled, [True]) | |
121 | ||
122 | class TestAfterEnd(unittest.TestCase): | |
123 | def _getTargetClass(self): | |
124 | from repoze.tm import AfterEnd | |
125 | return AfterEnd | |
126 | ||
127 | def _makeOne(self): | |
128 | return self._getTargetClass()() | |
129 | ||
130 | def test_register(self): | |
131 | registry = self._makeOne() | |
132 | func = lambda *x: None | |
133 | txn = DummyTransaction() | |
134 | registry.register(func, txn) | |
135 | self.assertEqual(getattr(txn, registry.key), [func]) | |
136 | ||
137 | def test_unregister_exists(self): | |
138 | registry = self._makeOne() | |
139 | func = lambda *x: None | |
140 | txn = DummyTransaction() | |
141 | registry.register(func, txn) | |
142 | self.assertEqual(getattr(txn, registry.key), [func]) | |
143 | registry.unregister(func, txn) | |
144 | self.failIf(hasattr(txn, registry.key)) | |
145 | ||
146 | def test_unregister_notexists(self): | |
147 | registry = self._makeOne() | |
148 | func = lambda *x: None | |
149 | txn = DummyTransaction() | |
150 | setattr(txn, registry.key, [None]) | |
151 | registry.unregister(func, txn) | |
152 | self.assertEqual(getattr(txn, registry.key), [None]) | |
153 | ||
154 | class UtilityFunctionTests(unittest.TestCase): | |
155 | def test_isActive(self): | |
156 | from repoze.tm import ekey | |
157 | from repoze.tm import isActive | |
158 | self.assertEqual(isActive({ekey:True}), True) | |
159 | self.assertEqual(isActive({}), False) | |
160 | ||
161 | class TestMakeTM(unittest.TestCase): | |
162 | def test_make_tm_withveto(self): | |
163 | from repoze.tm import make_tm | |
164 | tm = make_tm(DummyApplication(), {}, 'repoze.tm.tests:fakeveto') | |
165 | self.assertEqual(tm.commit_veto, fakeveto) | |
166 | ||
167 | def test_make_tm_noveto(self): | |
168 | from repoze.tm import make_tm | |
169 | tm = make_tm(DummyApplication(), {}, None) | |
170 | self.assertEqual(tm.commit_veto, None) | |
171 | ||
172 | def fakeveto(environ, status, headers): | |
173 | pass | |
174 | ||
175 | class DummyTransaction: | |
176 | pass | |
177 | ||
178 | 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 | |
183 | self.exception = exception | |
184 | self.register = register | |
185 | self.status = status | |
186 | ||
187 | def __call__(self, environ, start_response): | |
188 | 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 | if self.exception: | |
198 | raise ValueError('raising') | |
199 | return ['hello'] | |
200 | ||
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 | Metadata-Version: 1.0 | |
1 | Name: repoze.tm2 | |
2 | Version: 1.0a4 | |
3 | Summary: Zope-like transaction manager via WSGI middleware | |
4 | Home-page: http://www.repoze.org | |
5 | Author: Agendaless Consulting | |
6 | Author-email: repoze-dev@lists.repoze.org | |
7 | License: BSD-derived (http://www.repoze.org/LICENSE.txt) | |
8 | Description: repoze.tm2 (Transaction Manager) | |
9 | =============================== | |
10 | ||
11 | Middleware which uses the ZODB transaction manager to wrap a call to | |
12 | its pipeline children inside a transaction. This is a fork of the | |
13 | ``repoze.tm`` package which depends only on the ``transaction`` | |
14 | package rather than the entirety of ZODB (for users who don't rely on ZODB). | |
15 | ||
16 | See docs/index.rst for documentation. | |
17 | ||
18 | ||
19 | 1.0a4 (2009/1/6) | |
20 | ================ | |
21 | ||
22 | - RESTify CHANGES, move docs in README.txt into Sphinx. | |
23 | ||
24 | - Remove ``setup.cfg`` (all dependencies available via PyPI). | |
25 | ||
26 | - Synchronization point with ``repoze.tm`` (0.9). | |
27 | ||
28 | 1.0a3 | |
29 | ===== | |
30 | ||
31 | Allow ``commit_veto`` hook to be specified within Paste config, ala:: | |
32 | ||
33 | [filter:tm] | |
34 | use = repoze.tm:make_tm | |
35 | commit_veto = some.package:myfunction | |
36 | ||
37 | ``myfunction`` should take three args: environ, status, headers and | |
38 | should return True if the txn should be aborted, False if it should be | |
39 | committed. | |
40 | ||
41 | Initial PyPI release. | |
42 | ||
43 | 1.0a2 (2008/7/15) | |
44 | ================= | |
45 | ||
46 | - Provide "commit_veto" hook point (contributed by Alberto Valverde). | |
47 | ||
48 | - Point easy_install at http://dist.repoze.org/tm2/dev/simple via setup.cfg. | |
49 | ||
50 | 1.0a1 | |
51 | ===== | |
52 | ||
53 | - Fork point: we've created repoze.tm2, which is repoze.tm that has a | |
54 | dependency only on the 'transaction' package instead of all of ZODB. | |
55 | ||
56 | - Better documentation for non-Zope usage in README.txt. | |
57 | ||
58 | 0.8 | |
59 | === | |
60 | ||
61 | - Relaxed requirement for ZODB 3.7.2, since we might need to use the | |
62 | package with other verions. Note that the tests which depend on | |
63 | transaction having "doom" semantics don't work with 3.7.2, anyway. | |
64 | ||
65 | 0.7 | |
66 | === | |
67 | ||
68 | - Depend on PyPI release of ZODB 3.7.2. Upgrade to this by doing | |
69 | bin/easy_install -U 'ZODB3 >= 3.7.1, < 3.8.0a' if necessary. | |
70 | ||
71 | 0.6 | |
72 | === | |
73 | ||
74 | - after_end.register and after_end.unregister must now be passed a | |
75 | transaction object rather than a WSGI environment to avoid the | |
76 | possibility that the WSGI environment used by a child participating | |
77 | in transaction management won't be the same one used by the | |
78 | repoze.tm package. | |
79 | ||
80 | - repoze.tm now inserts a key into the WSGI environment | |
81 | (``repoze.tm.active``) if it's active in the WSGI pipeline. An API | |
82 | function, repoze.tm:isActive can be called with a single argument, | |
83 | the WSGI environment, to check if the middleware is active. | |
84 | ||
85 | 0.5 | |
86 | === | |
87 | ||
88 | - Depend on rerolled ZODB 3.7.1 instead of zopelib. | |
89 | ||
90 | - Add license and copyright, change trove classifiers. | |
91 | ||
92 | 0.4 | |
93 | === | |
94 | ||
95 | - Depend on zopelib rather than ZODB 3.8.0b3 distribution, because the | |
96 | ZODB distribution pulls in various packages (zope.interface and ZEO | |
97 | most notably) that are incompatible with stock Zope 2.10.4 apps and | |
98 | older sandboxes. We'll need to revisit this. | |
99 | ||
100 | 0.3 | |
101 | === | |
102 | ||
103 | - Provide limited compatibility for older transaction package versions | |
104 | which don't support the 'transaction.isDoomed' API. | |
105 | ||
106 | 0.2 | |
107 | === | |
108 | ||
109 | - Provide after_end API for registering callbacks at transaction end. | |
110 | ||
111 | 0.1 | |
112 | === | |
113 | ||
114 | - Initial Release | |
115 | ||
116 | Keywords: web application server wsgi zope repoze | |
117 | Platform: UNKNOWN | |
118 | Classifier: Development Status :: 3 - Alpha | |
119 | Classifier: Intended Audience :: Developers | |
120 | Classifier: Programming Language :: Python | |
121 | Classifier: Topic :: Internet :: WWW/HTTP | |
122 | Classifier: Topic :: Internet :: WWW/HTTP :: WSGI | |
123 | Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware |
0 | CHANGES.txt | |
1 | COPYRIGHT.txt | |
2 | LICENSE.txt | |
3 | README.txt | |
4 | TODO.txt | |
5 | setup.py | |
6 | docs/Makefile | |
7 | docs/changes.rst | |
8 | docs/conf.py | |
9 | docs/index.rst | |
10 | docs/.static/logo_hi.gif | |
11 | docs/.static/repoze.css | |
12 | repoze/__init__.py | |
13 | repoze.tm2.egg-info/PKG-INFO | |
14 | repoze.tm2.egg-info/SOURCES.txt | |
15 | repoze.tm2.egg-info/dependency_links.txt | |
16 | repoze.tm2.egg-info/entry_points.txt | |
17 | repoze.tm2.egg-info/namespace_packages.txt | |
18 | repoze.tm2.egg-info/not-zip-safe | |
19 | repoze.tm2.egg-info/requires.txt | |
20 | repoze.tm2.egg-info/top_level.txt | |
21 | repoze/tm/__init__.py | |
22 | repoze/tm/tests.py⏎ |
0 | repoze |
0 | transaction⏎ |
0 | repoze |
0 | __version__ = '1.0a4' | |
1 | ||
2 | import os | |
3 | from setuptools import setup, find_packages | |
4 | ||
5 | here = os.path.abspath(os.path.dirname(__file__)) | |
6 | README = open(os.path.join(here, 'README.txt')).read() | |
7 | CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() | |
8 | ||
9 | setup(name='repoze.tm2', | |
10 | version=__version__, | |
11 | description='Zope-like transaction manager via WSGI middleware', | |
12 | long_description=README + "\n\n" + CHANGES, | |
13 | classifiers=[ | |
14 | "Development Status :: 3 - Alpha", | |
15 | "Intended Audience :: Developers", | |
16 | "Programming Language :: Python", | |
17 | "Topic :: Internet :: WWW/HTTP", | |
18 | "Topic :: Internet :: WWW/HTTP :: WSGI", | |
19 | "Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware", | |
20 | ], | |
21 | keywords='web application server wsgi zope repoze', | |
22 | author="Agendaless Consulting", | |
23 | author_email="repoze-dev@lists.repoze.org", | |
24 | url="http://www.repoze.org", | |
25 | license="BSD-derived (http://www.repoze.org/LICENSE.txt)", | |
26 | packages=find_packages(), | |
27 | include_package_data=True, | |
28 | namespace_packages=['repoze'], | |
29 | zip_safe=False, | |
30 | install_requires=['transaction'], | |
31 | tests_require=['transaction'], | |
32 | test_suite = "repoze.tm.tests", | |
33 | entry_points=""" | |
34 | [paste.filter_app_factory] | |
35 | tm = repoze.tm:make_tm | |
36 | """, | |
37 | ) | |
38 |