././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1673913305.5942993 repoze.tm2-2.2.0/0000775000175000017500000000000000000000000014667 5ustar00tseavertseaver00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1464977267.0 repoze.tm2-2.2.0/.gitignore0000644000175000017500000000015000000000000016651 0ustar00tseavertseaver00000000000000*.pyc dist/ *.egg-info .coverage env26/ docs/.build/ .tox nosetests.xml coverage.xml docs/_build build/ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673913267.0 repoze.tm2-2.2.0/CHANGES.rst0000664000175000017500000001161100000000000016471 0ustar00tseavertseaver00000000000000Changelog ========= 2.2.0 (2023-01-16) ------------------ - Add support for Python 3.7, 3.8, 3.9, 3.10, and 3.11. - Drop suppport for Python 2.7, 3.4, 3.5 and 3.6. - Add Github Actions workflow running unit tests / coverage for PRs. 2.1 (2016-06-03) ---------------- - Add support for Python 3.4, 3.5 and PyPy3. - Drop support for Python 2.6 and 3.2. - Add support for testing under Travis. 2.0 (2013-06-26) ---------------- - Avoid swallowing the original exception while aborting the transaction in middleware. See PR #3. 2.0b1 (2013-04-05) ------------------ - Middleware is now a generator, to deal appropriately with application iterators which are themselves not lists. - Convert use of deprecated failIf/failUnless to assertFalse/assertTrue. - Add support for testing under supported Pythons using Tox. - Add explicit support for Python 3.2 ad 3.3. - Drop support for Python 2.4, 2.5. 1.0 (2012-03-24) ---------------- - Run OOTB under Python 2.4 / 2.5 (pin 'transaction' dependency to a supported version when running under 2.4 / 2.5). 1.0b2 (2011-07-18) ------------------ - A new header ``X-Tm`` is now honored by the ``default_commit_veto`` commit veto hook. If this header exists in the headerlist, its value must be a string. If its value is ``commit``, the transaction will be committed regardless of the status code or the value of ``X-Tm-Abort``. If the value of the ``X-Tm`` header is ``abort`` (or any other string value except ``commit``), the transaction will be aborted regardless of the status code or the value of ``X-Tm-Abort``. - Use of the ``X-Tm-Abort`` header is now deprecated. Instead use the ``X-Tm`` header with a value of ``abort`` instead. - Add API docs section. 1.0b1 (2011-01-19) ------------------ - Added ``repoze.tm.default_commit_veto`` commit veto hook. This commit veto hook aborts for 4XX and 5XX response codes, or if there's a header named ``X-Tm-Abort`` in the headerlist and allows a commit otherwise. - Documented commit veto hook. 1.0a5 (2009-09-07) ------------------ - Don't commit after aborting if the transaction was doomed or if the commit veto aborted. - Don't use "real" transaction module in tests. - 100% test coverage. 1.0a4 (2009-01-06) ------------------ - RESTify CHANGES, move docs in README.txt into Sphinx. - Remove ``setup.cfg`` (all dependencies available via PyPI). - Synchronization point with ``repoze.tm`` (0.9). 1.0a3 (2008-08-03) ------------------ Allow ``commit_veto`` hook to be specified within Paste config, ala:: [filter:tm] use = repoze.tm:make_tm commit_veto = some.package:myfunction ``myfunction`` should take three args: environ, status, headers and should return True if the txn should be aborted, False if it should be committed. Initial PyPI release. 1.0a2 (2008-07-15) ------------------ - Provide "commit_veto" hook point (contributed by Alberto Valverde). - Point easy_install at http://dist.repoze.org/tm2/dev/simple via setup.cfg. 1.0a1 (2008-01-09) ------------------ - Fork point: we've created repoze.tm2, which is repoze.tm that has a dependency only on the 'transaction' package instead of all of ZODB. - Better documentation for non-Zope usage in README.txt. 0.8 (2007-10-11) ---------------- - Relaxed requirement for ZODB 3.7.2, since we might need to use the package with other verions. Note that the tests which depend on transaction having "doom" semantics don't work with 3.7.2, anyway. 0.7 (2007-09-25) ---------------- - Depend on PyPI release of ZODB 3.7.2. Upgrade to this by doing bin/easy_install -U 'ZODB3 >= 3.7.1, < 3.8.0a' if necessary. 0.6 (2007-09-21) ---------------- - after_end.register and after_end.unregister must now be passed a transaction object rather than a WSGI environment to avoid the possibility that the WSGI environment used by a child participating in transaction management won't be the same one used by the repoze.tm package. - repoze.tm now inserts a key into the WSGI environment (``repoze.tm.active``) if it's active in the WSGI pipeline. An API function, repoze.tm:isActive can be called with a single argument, the WSGI environment, to check if the middleware is active. 0.5 (2007-09-18) ---------------- - Depend on rerolled ZODB 3.7.1 instead of zopelib. - Add license and copyright, change trove classifiers. 0.4 (2007-09-17) ---------------- - Depend on zopelib rather than ZODB 3.8.0b3 distribution, because the ZODB distribution pulls in various packages (zope.interface and ZEO most notably) that are incompatible with stock Zope 2.10.4 apps and older sandboxes. We'll need to revisit this. 0.3 (2007-09-14) ---------------- - Provide limited compatibility for older transaction package versions which don't support the 'transaction.isDoomed' API. 0.2 (2007-09-13) ---------------- - Provide after_end API for registering callbacks at transaction end. 0.1 (2007-09-10) ---------------- - Initial Release ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1332631183.0 repoze.tm2-2.2.0/CONTRIBUTORS.txt0000644000175000017500000001113200000000000017361 0ustar00tseavertseaver00000000000000Repoze Project Contributor Agreement ==================================== The submitter agrees by adding his or her name within the section below named "Contributors" and submitting the resulting modified document to the canonical shared repository location for this software project (whether directly, as a user with "direct commit access", or via a "pull request"), he or she is signing a contract electronically. The submitter becomes a Contributor after a) he or she signs this document by adding their name beneath the "Contributors" section below, and b) the resulting document is accepted into the canonical version control repository. Treatment of Account --------------------- Contributor will not allow anyone other than the Contributor to use his or her username or source repository login to submit code to a Repoze Project source repository. Should Contributor become aware of any such use, Contributor will immediately by notifying Agendaless Consulting. Notification must be performed by sending an email to webmaster@agendaless.com. Until such notice is received, Contributor will be presumed to have taken all actions made through Contributor's account. If the Contributor has direct commit access, Agendaless Consulting will have complete control and discretion over capabilities assigned to Contributor's account, and may disable Contributor's account for any reason at any time. Legal Effect of Contribution ---------------------------- Upon submitting a change or new work to a Repoze Project source Repository (a "Contribution"), you agree to assign, and hereby do assign, a one-half interest of all right, title and interest in and to copyright and other intellectual property rights with respect to your new and original portions of the Contribution to Agendaless Consulting. You and Agendaless Consulting each agree that the other shall be free to exercise any and all exclusive rights in and to the Contribution, without accounting to one another, including without limitation, the right to license the Contribution to others under the Repoze Public License. This agreement shall run with title to the Contribution. Agendaless Consulting does not convey to you any right, title or interest in or to the Program or such portions of the Contribution that were taken from the Program. Your transmission of a submission to the Repoze Project source Repository and marks of identification concerning the Contribution itself constitute your intent to contribute and your assignment of the work in accordance with the provisions of this Agreement. License Terms ------------- Code committed to the Repoze Project source repository (Committed Code) must be governed by the Repoze Public License (http://repoze.org/LICENSE.txt, aka "the RPL") or another license acceptable to Agendaless Consulting. Until Agendaless Consulting declares in writing an acceptable license other than the RPL, only the RPL shall be used. A list of exceptions is detailed within the "Licensing Exceptions" section of this document, if one exists. Representations, Warranty, and Indemnification ---------------------------------------------- Contributor represents and warrants that the Committed Code does not violate the rights of any person or entity, and that the Contributor has legal authority to enter into this Agreement and legal authority over Contributed Code. Further, Contributor indemnifies Agendaless Consulting against violations. Cryptography ------------ Contributor understands that cryptographic code may be subject to government regulations with which Agendaless Consulting and/or entities using Committed Code must comply. Any code which contains any of the items listed below must not be checked-in until Agendaless Consulting staff has been notified and has approved such contribution in writing. - Cryptographic capabilities or features - Calls to cryptographic features - User interface elements which provide context relating to cryptography - Code which may, under casual inspection, appear to be cryptographic. Notices ------- Contributor confirms that any notices required will be included in any Committed Code. Licensing Exceptions ==================== None. List of Contributors ==================== The below-signed are contributors to a code repository that is part of the project named "repoze.tm2". Each below-signed contributor has read, understand and agrees to the terms above in the section within this document entitled "Repoze Project Contributor Agreement" as of the date beside his or her name. Contributors ------------ - Tres Seaver, 2011/02/22 - Stéphane Klein , 2011/06/18 - Brian Sutherland, 2011/07/11 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1298648313.0 repoze.tm2-2.2.0/COPYRIGHT.txt0000644000175000017500000000015500000000000016777 0ustar00tseavertseaver00000000000000Copyright (c) 2007 Agendaless Consulting and Contributors. (http://www.agendaless.com), All Rights Reserved ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1298648313.0 repoze.tm2-2.2.0/LICENSE.txt0000644000175000017500000000337700000000000016522 0ustar00tseavertseaver00000000000000License A copyright notice accompanies this license document that identifies the copyright holders. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1673913305.5942993 repoze.tm2-2.2.0/PKG-INFO0000664000175000017500000002253000000000000015766 0ustar00tseavertseaver00000000000000Metadata-Version: 2.1 Name: repoze.tm2 Version: 2.2.0 Summary: Per-request transactions via WSGI middleware Home-page: http://www.repoze.org Author: Agendaless Consulting Author-email: repoze-dev@lists.repoze.org License: BSD-derived (http://www.repoze.org/LICENSE.txt) Description: repoze.tm2 (Transaction Manager) ================================ .. image:: https://travis-ci.org/repoze/repoze.tm2.png?branch=master :target: https://travis-ci.org/repoze/repoze.tm2 .. image:: https://readthedocs.org/projects/repozetm2/badge/?version=latest :target: http://repozetm2.readthedocs.org/en/latest/ :alt: Documentation Status .. image:: https://img.shields.io/pypi/v/repoze.tm2.svg :target: https://pypi.python.org/pypi/repoze.tm2 .. image:: https://img.shields.io/pypi/pyversions/repoze.tm2.svg :target: https://pypi.python.org/pypi/repoze.tm2 Middleware which uses the ZODB transaction manager to wrap a call to its pipeline children inside a transaction. This is a fork of the ``repoze.tm`` package which depends only on the ``transaction`` package rather than the entirety of ZODB (for users who don't rely on ZODB). Installation ------------ Install using setuptools, e.g. (within a virtualenv):: $ easy_install repoze.tm2 or using pip:: $ pip install repoze.tm2 Usage ----- For details on using the various components, please see the documentation in ``docs/index.rst``. A rendered version of that documentation is also available online: - http://repozetm2.readthedocs.org/en/latest/ Reporting Bugs -------------- Please report bugs in this package to https://github.com/repoze/repoze.tm2/issues Obtaining Source Code --------------------- Download development or tagged versions of the software by visiting: https://github.com/repoze/repoze.tm2 Changelog ========= 2.2.0 (2023-01-16) ------------------ - Add support for Python 3.7, 3.8, 3.9, 3.10, and 3.11. - Drop suppport for Python 2.7, 3.4, 3.5 and 3.6. - Add Github Actions workflow running unit tests / coverage for PRs. 2.1 (2016-06-03) ---------------- - Add support for Python 3.4, 3.5 and PyPy3. - Drop support for Python 2.6 and 3.2. - Add support for testing under Travis. 2.0 (2013-06-26) ---------------- - Avoid swallowing the original exception while aborting the transaction in middleware. See PR #3. 2.0b1 (2013-04-05) ------------------ - Middleware is now a generator, to deal appropriately with application iterators which are themselves not lists. - Convert use of deprecated failIf/failUnless to assertFalse/assertTrue. - Add support for testing under supported Pythons using Tox. - Add explicit support for Python 3.2 ad 3.3. - Drop support for Python 2.4, 2.5. 1.0 (2012-03-24) ---------------- - Run OOTB under Python 2.4 / 2.5 (pin 'transaction' dependency to a supported version when running under 2.4 / 2.5). 1.0b2 (2011-07-18) ------------------ - A new header ``X-Tm`` is now honored by the ``default_commit_veto`` commit veto hook. If this header exists in the headerlist, its value must be a string. If its value is ``commit``, the transaction will be committed regardless of the status code or the value of ``X-Tm-Abort``. If the value of the ``X-Tm`` header is ``abort`` (or any other string value except ``commit``), the transaction will be aborted regardless of the status code or the value of ``X-Tm-Abort``. - Use of the ``X-Tm-Abort`` header is now deprecated. Instead use the ``X-Tm`` header with a value of ``abort`` instead. - Add API docs section. 1.0b1 (2011-01-19) ------------------ - Added ``repoze.tm.default_commit_veto`` commit veto hook. This commit veto hook aborts for 4XX and 5XX response codes, or if there's a header named ``X-Tm-Abort`` in the headerlist and allows a commit otherwise. - Documented commit veto hook. 1.0a5 (2009-09-07) ------------------ - Don't commit after aborting if the transaction was doomed or if the commit veto aborted. - Don't use "real" transaction module in tests. - 100% test coverage. 1.0a4 (2009-01-06) ------------------ - RESTify CHANGES, move docs in README.txt into Sphinx. - Remove ``setup.cfg`` (all dependencies available via PyPI). - Synchronization point with ``repoze.tm`` (0.9). 1.0a3 (2008-08-03) ------------------ Allow ``commit_veto`` hook to be specified within Paste config, ala:: [filter:tm] use = repoze.tm:make_tm commit_veto = some.package:myfunction ``myfunction`` should take three args: environ, status, headers and should return True if the txn should be aborted, False if it should be committed. Initial PyPI release. 1.0a2 (2008-07-15) ------------------ - Provide "commit_veto" hook point (contributed by Alberto Valverde). - Point easy_install at http://dist.repoze.org/tm2/dev/simple via setup.cfg. 1.0a1 (2008-01-09) ------------------ - Fork point: we've created repoze.tm2, which is repoze.tm that has a dependency only on the 'transaction' package instead of all of ZODB. - Better documentation for non-Zope usage in README.txt. 0.8 (2007-10-11) ---------------- - Relaxed requirement for ZODB 3.7.2, since we might need to use the package with other verions. Note that the tests which depend on transaction having "doom" semantics don't work with 3.7.2, anyway. 0.7 (2007-09-25) ---------------- - Depend on PyPI release of ZODB 3.7.2. Upgrade to this by doing bin/easy_install -U 'ZODB3 >= 3.7.1, < 3.8.0a' if necessary. 0.6 (2007-09-21) ---------------- - after_end.register and after_end.unregister must now be passed a transaction object rather than a WSGI environment to avoid the possibility that the WSGI environment used by a child participating in transaction management won't be the same one used by the repoze.tm package. - repoze.tm now inserts a key into the WSGI environment (``repoze.tm.active``) if it's active in the WSGI pipeline. An API function, repoze.tm:isActive can be called with a single argument, the WSGI environment, to check if the middleware is active. 0.5 (2007-09-18) ---------------- - Depend on rerolled ZODB 3.7.1 instead of zopelib. - Add license and copyright, change trove classifiers. 0.4 (2007-09-17) ---------------- - Depend on zopelib rather than ZODB 3.8.0b3 distribution, because the ZODB distribution pulls in various packages (zope.interface and ZEO most notably) that are incompatible with stock Zope 2.10.4 apps and older sandboxes. We'll need to revisit this. 0.3 (2007-09-14) ---------------- - Provide limited compatibility for older transaction package versions which don't support the 'transaction.isDoomed' API. 0.2 (2007-09-13) ---------------- - Provide after_end API for registering callbacks at transaction end. 0.1 (2007-09-10) ---------------- - Initial Release Keywords: web application server wsgi zope repoze Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Internet :: WWW/HTTP :: WSGI Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware Provides-Extra: docs ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1464979298.0 repoze.tm2-2.2.0/README.rst0000664000175000017500000000300400000000000016353 0ustar00tseavertseaver00000000000000repoze.tm2 (Transaction Manager) ================================ .. image:: https://travis-ci.org/repoze/repoze.tm2.png?branch=master :target: https://travis-ci.org/repoze/repoze.tm2 .. image:: https://readthedocs.org/projects/repozetm2/badge/?version=latest :target: http://repozetm2.readthedocs.org/en/latest/ :alt: Documentation Status .. image:: https://img.shields.io/pypi/v/repoze.tm2.svg :target: https://pypi.python.org/pypi/repoze.tm2 .. image:: https://img.shields.io/pypi/pyversions/repoze.tm2.svg :target: https://pypi.python.org/pypi/repoze.tm2 Middleware which uses the ZODB transaction manager to wrap a call to its pipeline children inside a transaction. This is a fork of the ``repoze.tm`` package which depends only on the ``transaction`` package rather than the entirety of ZODB (for users who don't rely on ZODB). Installation ------------ Install using setuptools, e.g. (within a virtualenv):: $ easy_install repoze.tm2 or using pip:: $ pip install repoze.tm2 Usage ----- For details on using the various components, please see the documentation in ``docs/index.rst``. A rendered version of that documentation is also available online: - http://repozetm2.readthedocs.org/en/latest/ Reporting Bugs -------------- Please report bugs in this package to https://github.com/repoze/repoze.tm2/issues Obtaining Source Code --------------------- Download development or tagged versions of the software by visiting: https://github.com/repoze/repoze.tm2 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1298648313.0 repoze.tm2-2.2.0/TODO.txt0000644000175000017500000000043700000000000016177 0ustar00tseavertseaver00000000000000Investigate using the absurd API for transaction synchronizers e.g.: class RepozeTMSync: def beforeCompletion(self, transaction): for thing in self.before: thing() def afterCompletion(self, transaction): for thing in self.after: thing() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1673913305.5902994 repoze.tm2-2.2.0/docs/0000775000175000017500000000000000000000000015617 5ustar00tseavertseaver00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1381883278.0 repoze.tm2-2.2.0/docs/Makefile0000644000175000017500000000434000000000000017256 0ustar00tseavertseaver00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html web pickle htmlhelp latex changes linkcheck help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files (usable by e.g. sphinx-web)" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf _build/* html: mkdir -p _build/html _build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in _build/html." pickle: mkdir -p _build/pickle _build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle @echo @echo "Build finished; now you can process the pickle files or run" @echo " sphinx-web _build/pickle" @echo "to start the sphinx-web server." web: pickle htmlhelp: mkdir -p _build/htmlhelp _build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex @echo @echo "Build finished; the LaTeX files are in _build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p _build/changes _build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo @echo "The overview file is in _build/changes." linkcheck: mkdir -p _build/linkcheck _build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in _build/linkcheck/output.txt." ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1673913305.5902994 repoze.tm2-2.2.0/docs/_static/0000775000175000017500000000000000000000000017245 5ustar00tseavertseaver00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1418577632.0 repoze.tm2-2.2.0/docs/_static/logo_hi.png0000664000175000017500000000653600000000000021405 0ustar00tseavertseaver00000000000000PNG  IHDR4LqsRGBPLTEpolܵEEE999VVUŪ؉ǼʤჂ򠞙dcaRRQ]\\{zx???MLL444333#7bKGDH pHYs  tIME  s IDAThXi:OA\\R [Yy%9 4SL?MtO&i6$ RV[% lL+[RA6aհı"U` iY.0U*d5_)/d<`#NbD̠*8=(gjȉ+P ޅD<U+G3\#xFij(++@|AM+[(fDv{30" R@<7:@ap#WLgw1aƆʈ&XW[ZZW;QT7Ap| [^EZYA" +5 YXS!n0#3E ~U#܅dV.pn#)0=g~nJ6gGI YDDžL>ev\ͨU伦v# {Ƃh&'nTKA6YQ%/ɋ߉]ϲ!F[43:3؁Ehq5Wgv3ˌVSU ;mJ0y1\ i>w)7veBz~ `P" `m>B){䓵fQ_I-`^h/rDx˘)0sqG5JiX`'`9VC | ޖ6ZY:E:"H{!G}JExpZa[~ D{+xl9Sȷ[xIAҜ( h$.E~;%"ԣ0؉/ "M,ip '* q KXުu+HZ ޞt҃yavp(*Ո*=|[-|lB] cR5sg0<`(x̷-KG&ZDPN-V@e¡,%ᵐ )Is[[&Bx}',ૐeO5B'Xl UXq=b V@Wkf{.U3 niclL ;1@κTSϓN9xä4UbpyIeIrgr{Sƀޔ!ԜԇHhz q@mAx/|=gx@~ 2fRh|>I!tfC9+8򐡥\f^[ƯVQUwēǞ|x&p`zXi?̙ q%])GJIc(F)m5zv{h7 q5j%r+jv[T!t:(m4kW*ȕSQPkv!UG5ǯDG:t_ۿr%KJv$CO␦b]{_=(rң){oUnhYFhV7NFAl;DoPh;35吺g)kxeL3FYFy~8pҳ__\寮0kIENDB`././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1298648313.0 repoze.tm2-2.2.0/docs/_static/repoze.css0000644000175000017500000000044600000000000021265 0ustar00tseavertseaver00000000000000@import url('default.css'); body { background-color: #006339; } div.document { background-color: #dad3bd; } div.sphinxsidebar h3,h4,h5,li,a { color: #127c56 !important; } div.related { color: #dad3bd; background-color: #00744a; } div.related a { color: #dad3bd; } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1418578530.0 repoze.tm2-2.2.0/docs/api.rst0000644000175000017500000000026600000000000017124 0ustar00tseavertseaver00000000000000:mod:`repoze.tm2` API --------------------- .. automodule:: repoze.tm .. autofunction:: default_commit_veto .. autoclass:: TM .. autoclass:: AfterEnd .. attribute:: after_end ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1418578541.0 repoze.tm2-2.2.0/docs/changes.rst0000644000175000017500000000013700000000000017760 0ustar00tseavertseaver00000000000000:mod:`repoze.tm2` Change History ================================ .. include:: ../CHANGES.rst ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1418577655.0 repoze.tm2-2.2.0/docs/conf.py0000644000175000017500000001357500000000000017127 0ustar00tseavertseaver00000000000000# -*- coding: utf-8 -*- # # repoze.tm documentation build configuration file # # This file is execfile()d with the current directory set to its containing # dir. # # The contents of this file are pickled, so don't put values in the # namespace that aren't pickleable (module imports are okay, they're # removed automatically). # # All configuration values have a default value; values that are commented # out serve to show the default value. import sys, os # If your extensions are in another directory, add it here. If the # directory is relative to the documentation root, use os.path.abspath to # make it absolute, like shown here. #sys.path.append(os.path.abspath('some/directory')) parent = os.path.dirname(os.path.dirname(__file__)) sys.path.append(os.path.abspath(parent)) wd = os.getcwd() os.chdir(parent) os.system('%s setup.py test -q' % sys.executable) os.chdir(wd) for item in os.listdir(parent): if item.endswith('.egg'): sys.path.append(os.path.join(parent, item)) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General substitutions. project = 'repoze.tm2' copyright = '2008-2013, Repoze Developers ' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = '2.0' # The full version, including alpha/beta/rc tags. release = '2.0' # There are two options for replacing |today|: either, you set today to # some non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directories, that shouldn't be # searched for source files. #exclude_dirs = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'repoze.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as # html_title. #html_short_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. html_logo = '_static/logo_hi.png' # The name of an image file (within the static path) to use as favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or # 32x32 pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) # here, relative to this directory. They are copied after the builtin # static files, so a file named "default.css" will overwrite the builtin # "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, the reST sources are included in the HTML build as # _sources/. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages # will contain a tag referring to it. The value of this option must # be the base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'tmdoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, document class [howto/manual]). latex_documents = [ ('index', 'tm.tex', 'repoze.tm Documentation', 'Repoze Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the # top of the title page. latex_logo = '_static/logo_hi.png' # For "manual" documents, if this is true, then toplevel headings are # parts, not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1418594941.0 repoze.tm2-2.2.0/docs/index.rst0000644000175000017500000003605500000000000017467 0ustar00tseavertseaver00000000000000Documentation for repoze.tm2 (``repoze.tm`` fork) ================================================= Overview -------- :mod:`repoze.tm2` is WSGI middleware which uses the ``ZODB`` package's transaction manager to wrap a call to its pipeline children inside a transaction. .. note:: :mod:`repoze.tm2` is equivalent to the :mod:`repoze.tm` package (it was forked from :mod:`repoze.tm`), except it has a dependency only on the ``transaction`` package rather than a dependency on the entire ``ZODB3`` package (``ZODB3`` 3.8 ships with the ``transaction`` package right now). It is an error to install both repoze.tm and repoze.tm2 into the same environment, as they provide the same entry points and import points. Behavior -------- When this middleware is present in the WSGI pipeline, a new transaction will be started once a WSGI request makes it to the middleware. If any downstream application raises an exception, the transaction will be aborted, otherwise the transaction will be committed. Any "data managers" participating in the transaction will be aborted or committed respectively. A ZODB "connection" is an example of a data manager. Since this is a tiny wrapper around the ZODB transaction module, and the ZODB transaction module is "thread-safe" (in the sense that its default policy is to create a new transaction for each thread), it should be fine to use in either multiprocess or multithread environments. Purpose and Usage ----------------- The ZODB transaction manager is a completely generic transaction manager. It can be used independently of the actual "object database" part of ZODB. One of the purposes of creating :mod:`repoze.tm` was to allow for systems other than Zope to make use of two-phase commit transactions in a WSGI context. Let's pretend we have an existing system that places data into a relational database when someone submits a form. The system has been running for a while, and our code handles the database commit and rollback for us explicitly; if the form processing succeeds, our code commits the database transaction. If it fails, our code rolls back the database transaction. Everything works fine. Now our customer asks us if we can also place data into another separate relational database when the form is submitted as well as continuing to place data in the original database. We need to put data in both databases, and if we want to ensure that no records exist in one that don't exist in the other as a result of a form submission, we're going to need to do a pretty complicated commit and rollback dance in each place in our code which needs to write to both data stores. We can't just blindly commit one, then commit the other, because the second commit may fail and we'll be left with "orphan" data in the first, and we'll either need to clean it up manually or leave it there to trip over later. A transaction manager helps us ensure that no data is committed to either database unless both participating data stores can commit. Once the transaction manager determines that both data stores are willing to commit, it will commit them both in very quick succession, so that there is only a minimal chance that the second data store will fail to commit. If it does, the system will raise an error that makes it impossible to begin another transaction until the system restarts, so the damage is minimized. In practice, this error almost never occurs unless the code that interfaces the database to the transaction manager has a bug. Adding :mod:`repoze.tm2` To Your WSGI Pipeline ---------------------------------------------- Via ``PasteDeploy`` .INI configuration:: [pipeline:main] pipeline = egg:repoze.tm2#tm myapp Via Python: .. code-block:: python from otherplace import mywsgiapp from repoze.tm import TM new_wsgiapp = TM(mywsgiapp) Using A Commit Veto ------------------- If you'd like to veto commits based on the status code returned by the downstream application, use a commit veto callback. First, define the callback somewhere in your application: .. code-block:: python def commit_veto(environ, status, headers): for header_name, header_value in headers: if header_name.lower() == 'x-tm': if header_value.lower() == 'commit': return False return True for bad in ('4', '5'): if status.startswith(bad): return True return False Then configure it into your middleware. Via Python: .. code-block:: python from otherplace import mywsgiapp from my.package import commit_veto from repoze.tm import TM new_wsgiapp = TM(mywsgiapp, commit_veto=commit_veto) Via PasteDeploy: .. code-block:: ini [filter:tm] commit_veto = my.package:commit_veto In the PasteDeploy example, the path is a Python dotted name, where the dots separate module and package names, and the colon separates a module from its contents. In the above example, the code would be implemented as a "commit_veto" function which lives in the "package" submodule of the "my" package. A variant of the commit veto implementation shown above as an example is actually present in the ``repoze.tm2`` package as ``repoze.tm.default_commit_veto``. It's fairly general, so you needn't implement one yourself. Instead just use it. Via Python: .. code-block:: python from otherplace import mywsgiapp from repoze.tm import default_commit_veto from repoze.tm import TM new_wsgiapp = TM(mywsgiapp, commit_veto=default_commit_veto) Via PasteDeploy: .. code-block:: ini [filter:tm] commit_veto = repoze.tm:default_commit_veto API documentation for ``default_commit_veto`` exists at :func:`repoze.tm.default_commit_veto`. Mocking Up A Data Manager ------------------------- The piece of code you need to write in order to participate in ZODB transactions is called a 'data manager'. It is typically a class. Here's the interface that you need to implement in the code for a data manager: .. code-block:: python class IDataManager(zope.interface.Interface): """Objects that manage transactional storage. These objects may manage data for other objects, or they may manage non-object storages, such as relational databases. For example, a ZODB.Connection. Note that when some data is modified, that data's data manager should join a transaction so that data can be committed when the user commits the transaction. """ transaction_manager = zope.interface.Attribute( """The transaction manager (TM) used by this data manager. This is a public attribute, intended for read-only use. The value is an instance of ITransactionManager, typically set by the data manager's constructor. """ ) def abort(transaction): """Abort a transaction and forget all changes. Abort must be called outside of a two-phase commit. Abort is called by the transaction manager to abort transactions that are not yet in a two-phase commit. """ # Two-phase commit protocol. These methods are called by # the ITransaction object associated with the transaction # being committed. The sequence of calls normally follows # this regular expression: tpc_begin commit tpc_vote # (tpc_finish | tpc_abort) def tpc_begin(transaction): """Begin commit of a transaction, starting the two-phase commit. transaction is the ITransaction instance associated with the transaction being committed. """ def commit(transaction): """Commit modifications to registered objects. Save changes to be made persistent if the transaction commits (if tpc_finish is called later). If tpc_abort is called later, changes must not persist. This includes conflict detection and handling. If no conflicts or errors occur, the data manager should be prepared to make the changes persist when tpc_finish is called. """ def tpc_vote(transaction): """Verify that a data manager can commit the transaction. This is the last chance for a data manager to vote 'no'. A data manager votes 'no' by raising an exception. transaction is the ITransaction instance associated with the transaction being committed. """ def tpc_finish(transaction): """Indicate confirmation that the transaction is done. Make all changes to objects modified by this transaction persist. transaction is the ITransaction instance associated with the transaction being committed. This should never fail. If this raises an exception, the database is not expected to maintain consistency; it's a serious error. """ def tpc_abort(transaction): """Abort a transaction. This is called by a transaction manager to end a two-phase commit on the data manager. Abandon all changes to objects modified by this transaction. transaction is the ITransaction instance associated with the transaction being committed. This should never fail. """ def sortKey(): """Return a key to use for ordering registered DataManagers. ZODB uses a global sort order to prevent deadlock when it commits transactions involving multiple resource managers. The resource manager must define a sortKey() method that provides a global ordering for resource managers. """ # Alternate version: #"""Return a consistent sort key for this connection. # #This allows ordering multiple connections that use the same storage in #a consistent manner. This is unique for the lifetime of a connection, #which is good enough to avoid ZEO deadlocks. #""" Let's implement a mock data manager. Our mock data manager will write data to a file if the transaction commits. It will not write data to a file if the transaction aborts: .. code-block:: python class MockDataManager: transaction_manager = None def __init__(self, data, path): self.data = data self.path = path def abort(self, transaction): pass def tpc_begin(self, transaction): pass def commit(self, transaction): import tempfile self.tempfn = tempfile.mktemp() temp = open(self.tempfn, 'wb') temp.write(self.data) temp.flush() temp.close() def tpc_vote(self, transaction): import os if not os.path.exists(self.tempfn): raise ValueError('%s doesnt exist' % self.tempfn) if os.path.exists(self.path): raise ValueError('file already exists') def tpc_finish(self, transaction): import os os.rename(self.tempfn, self.path) def tpc_abort(self, transaction): import os try: os.remove(self.tempfn) except OSError: pass We can create a datamanager and join it into the currently running transaction: .. code-block:: python dm = MockDataManager('heres the data', '/tmp/file') import transaction t = transaction.get() t.join(dm) When the transaction commits, a file will be placed in '/tmp/file' containing 'heres the data'. If the transaction aborts, no file will be created. If more than one data manager is joined to the transaction, all of them must be willing to commit or the entire transaction is aborted and none of them commit. If you can imagine creating two of the mock data managers we've made within application code, if one has a problem during "tpc_vote", neither will actually write a file to the ultimate location, and thus your application consistency is maintained. Integrating Your Data Manager With :mod:`repoze.tm2` ---------------------------------------------------- The :mod:`repoze.tm2` transaction management machinery has an implicit policy. When it is in the WSGI pipeline, a transaction is started when the middleware is invoked. Thus, in your application code, calling "import transaction; transaction.get()" will return the transaction object created by the :mod:`repoze.tm2` middleware. You needn't call t.commit() or t.abort() within your application code. You only need to call t.join, to register your data manager with the transaction. :mod:`repoze.tm2` will abort the transaction if an exception is raised by your application code or lower middleware before it returns a WSGI response. If your application or lower middleware raises an exception, the transaction is aborted. Cleanup ------- When the :mod:`repoze.tm2` middleware is in the WSGI pipeline, a boolean key is present in the environment (``repoze.tm.active``). A utility function named :func:`repoze.tm.isActive` can be imported and passed the WSGI environment to check for activation: .. code-block:: python from repoze.tm import isActive tm_active = isActive(wsgi_environment) If an application needs to perform an action after a transaction ends, the :attr:`repoze.tm.after_end` registry may be used to register a callback. This object is an instance fo the :class:`repoze.tm.AfterEnd` class. The :meth:`repoze.tm.AfterEnd.register` method accepts a callback (accepting no arguments) and a transaction instance: .. code-block:: python from repoze.tm import after_end import transaction t = transaction.get() # the current transaction def func(): pass # close a connection, etc after_end.register(func, t) "after_end" callbacks should only be registered when the transaction manager is active, or a memory leak will result (registration cleanup happens only on transaction commit or abort, which is managed by :mod:`repoze.tm2` while in the pipeline). Further Documentation --------------------- Many database adapters written for Zope (e.g. for Postgres, MySQL, etc) use this transaction manager, so it should be possible to take a look in these places to see how to implement a more real-world transaction-aware database connector that uses this module in non-Zope applications: - http://www.zodb.org/en/latest/documentation/guide/transactions.html - http://mysql-python.sourceforge.net/ (ZMySQLDA) - http://www.initd.org/svn/psycopg/psycopg2/trunk/ (ZPsycoPGDA) Contacting ---------- The `repoze-dev maillist `_ should be used for communications about this software. Report bugs on Github: https://github.com/repoze/repoze.tm2/issues Fork it on Github: https://github.com/repoze/repoze.tm2/ API Docs ------------------------ .. toctree:: :maxdepth: 3 api Change Logs ----------- .. toctree:: :maxdepth: 2 changes Indices and tables ------------------ * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1673913305.5902994 repoze.tm2-2.2.0/repoze/0000775000175000017500000000000000000000000016173 5ustar00tseavertseaver00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1332630650.0 repoze.tm2-2.2.0/repoze/__init__.py0000644000175000017500000000011100000000000020273 0ustar00tseavertseaver00000000000000# repoze package __import__('pkg_resources').declare_namespace(__name__) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1673913305.5942993 repoze.tm2-2.2.0/repoze/tm/0000775000175000017500000000000000000000000016613 5ustar00tseavertseaver00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673910298.0 repoze.tm2-2.2.0/repoze/tm/__init__.py0000664000175000017500000001135500000000000020731 0ustar00tseavertseaver00000000000000# repoze TransactionManager WSGI middleware import sys import transaction ekey = 'repoze.tm.active' def reraise(tp, value, tb=None): if value.__traceback__ is not tb: # pragma: NO COVER raise value.with_traceback(tb) raise value class TM: """ Transaction management WSGI middleware """ def __init__(self, application, commit_veto=None): self.application = application self.commit_veto = commit_veto self.transaction = transaction # for testing def __call__(self, environ, start_response): transaction = self.transaction environ[ekey] = True transaction.begin() ctx = {} def save_status_and_headers(status, headers, exc_info=None): ctx.update(status=status, headers=headers) return start_response(status, headers, exc_info) try: for chunk in self.application(environ, save_status_and_headers): yield chunk except Exception: """Saving the exception""" try: type_, value, tb = sys.exc_info() self.abort() reraise(type_, value, tb) finally: del type_, value, tb # ZODB 3.8 + has isDoomed if hasattr(transaction, 'isDoomed') and transaction.isDoomed(): self.abort() else: if self.commit_veto is not None: try: status, headers = ctx['status'], ctx['headers'] veto = self.commit_veto(environ, status, headers) except: self.abort() raise if veto: self.abort() else: self.commit() else: self.commit() def commit(self): t = self.transaction.get() t.commit() after_end.cleanup(t) def abort(self): t = self.transaction.get() t.abort() after_end.cleanup(t) def isActive(environ): """ Return True if the ``repoze.tm.active`` key is in the WSGI environment passed as ``environ``, otherwise return ``False``.""" if ekey in environ: return True return False # Callback registry API helper class class AfterEnd: """ Callback registry API helper class. Use the singleton instance ``repoze.tm.after_end`` when possible.""" key = '_repoze_tm_afterend' def register(self, func, txn): funcs = getattr(txn, self.key, None) if funcs is None: funcs = [] setattr(txn, self.key, funcs) funcs.append(func) def unregister(self, func, txn): funcs = getattr(txn, self.key, None) if funcs is None: return new = [] for f in funcs: if f is func: continue new.append(f) if new: setattr(txn, self.key, new) else: delattr(txn, self.key) def cleanup(self, txn): funcs = getattr(txn, self.key, None) if funcs is not None: for func in funcs: func() delattr(txn, self.key) # singleton, importable by other modules after_end = AfterEnd() def default_commit_veto(environ, status, headers): """ When used as a commit veto, the logic in this function will cause the transaction to be committed if: - An ``X-Tm`` header with the value ``commit`` exists. If an ``X-Tm`` header with the value ``commit`` does not exist, the transaction will be aborted, if: - An ``X-Tm`` header with the value ``abort`` (or any value other than ``commit``) exists. - An ``X-Tm-Abort`` header exists with any value (for backwards compatibility; prefer ``X-Tm=abort`` in new code). - The status code starts with ``4`` or ``5``. Otherwise the transaction will be committed by default. """ abort_compat = False for header_name, header_value in headers: header_name = header_name.lower() if header_name == 'x-tm': header_value = header_value.lower() if header_value == 'commit': return False return True # x-tm always honored before x-tm-abort 1.0b1 compatibility if header_name == 'x-tm-abort': abort_compat = True if abort_compat: return True for bad in ('4', '5'): if status.startswith(bad): return True return False def make_tm(app, global_conf, commit_veto=None): """ Paste filter_app_factory entry point for creation of a TM middleware.""" from pkg_resources import EntryPoint if commit_veto is not None: commit_veto = EntryPoint.parse('x=%s' % commit_veto).resolve() return TM(app, commit_veto) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1332631183.0 repoze.tm2-2.2.0/repoze/tm/tests.py0000644000175000017500000002311600000000000020330 0ustar00tseavertseaver00000000000000import unittest class TestTM(unittest.TestCase): def _getTargetClass(self): from repoze.tm import TM return TM def _start_response(self, status, headers, exc_info=None): pass def _makeOne(self, app, commit_veto=None): return self._getTargetClass()(app, commit_veto) def test_ekey_inserted(self): app = DummyApplication() tm = self._makeOne(app) tm.transaction = DummyTransactionModule() from repoze.tm import ekey env = {} result = [chunk for chunk in tm(env, self._start_response)] self.assertEqual(result, ['hello']) self.assertTrue(ekey in env) def test_committed(self): app = DummyApplication() tm = self._makeOne(app) transaction = DummyTransactionModule() tm.transaction = transaction result = [chunk for chunk in tm({}, self._start_response)] self.assertEqual(result, ['hello']) self.assertEqual(transaction.committed, True) self.assertEqual(transaction.aborted, False) def test_aborted_via_doom(self): app = DummyApplication() tm = self._makeOne(app) transaction = DummyTransactionModule(doom=True) tm.transaction = transaction result = [chunk for chunk in tm({}, self._start_response)] self.assertEqual(result, ['hello']) self.assertEqual(transaction.committed, False) self.assertEqual(transaction.aborted, True) def test_aborted_via_exception(self): app = DummyApplication(exception=True) tm = self._makeOne(app) transaction = DummyTransactionModule() tm.transaction = transaction def execute_request(): [chunk for chunk in tm({}, self._start_response)] self.assertRaises(ValueError, execute_request) self.assertEqual(transaction.committed, False) self.assertEqual(transaction.aborted, True) def test_aborted_via_exception_and_doom(self): app = DummyApplication(exception=True) tm = self._makeOne(app) transaction = DummyTransactionModule(doom=True) tm.transaction = transaction def execute_request(): [chunk for chunk in tm({}, self._start_response)] self.assertRaises(ValueError, execute_request) self.assertEqual(transaction.committed, False) self.assertEqual(transaction.aborted, True) def test_aborted_via_commit_veto(self): app = DummyApplication(status="403 Forbidden") def commit_veto(environ, status, headers): self.assertTrue(isinstance(environ, dict), "environ is not passed properly") self.assertTrue(isinstance(headers, list), "headers are not passed properly") self.assertTrue(isinstance(status, str), "status is not passed properly") return not (200 <= int(status.split()[0]) < 400) tm = self._makeOne(app, commit_veto) transaction = DummyTransactionModule() tm.transaction = transaction [chunk for chunk in tm({}, self._start_response)] self.assertEqual(transaction.committed, False) self.assertEqual(transaction.aborted, True) # ici def test_committed_via_commit_veto_exception(self): app = DummyApplication(status="403 Forbidden") def commit_veto(environ, status, headers): return None tm = self._makeOne(app, commit_veto) transaction = DummyTransactionModule() tm.transaction = transaction [chunk for chunk in tm({}, self._start_response)] self.assertEqual(transaction.committed, True) self.assertEqual(transaction.aborted, False) def test_aborted_via_commit_veto_exception(self): app = DummyApplication(status="403 Forbidden") def commit_veto(environ, status, headers): raise ValueError('foo') tm = self._makeOne(app, commit_veto) transaction = DummyTransactionModule() tm.transaction = transaction def execute_request(): [chunk for chunk in tm({}, self._start_response)] self.assertRaises(ValueError, execute_request) self.assertEqual(transaction.committed, False) self.assertEqual(transaction.aborted, True) def test_cleanup_on_commit(self): from repoze.tm import after_end dummycalled = [] def dummy(): dummycalled.append(True) env = {} app = DummyApplication() tm = self._makeOne(app) transaction = DummyTransactionModule() setattr(transaction, after_end.key, [dummy]) tm.transaction = transaction [chunk for chunk in tm(env, self._start_response)] self.assertEqual(transaction.committed, True) self.assertEqual(transaction.aborted, False) self.assertEqual(dummycalled, [True]) def test_cleanup_on_abort(self): from repoze.tm import after_end dummycalled = [] def dummy(): dummycalled.append(True) env = {} app = DummyApplication(exception=True) tm = self._makeOne(app) transaction = DummyTransactionModule() setattr(transaction, after_end.key, [dummy]) tm.transaction = transaction def execute_request(): [chunk for chunk in tm(env, self._start_response)] self.assertRaises(ValueError, execute_request) self.assertEqual(transaction.committed, False) self.assertEqual(transaction.aborted, True) self.assertEqual(dummycalled, [True]) class TestAfterEnd(unittest.TestCase): def _getTargetClass(self): from repoze.tm import AfterEnd return AfterEnd def _makeOne(self): return self._getTargetClass()() def test_register(self): registry = self._makeOne() func = lambda *x: None txn = Dummy() registry.register(func, txn) self.assertEqual(getattr(txn, registry.key), [func]) def test_unregister_exists(self): registry = self._makeOne() func = lambda *x: None txn = Dummy() registry.register(func, txn) self.assertEqual(getattr(txn, registry.key), [func]) registry.unregister(func, txn) self.assertFalse(hasattr(txn, registry.key)) def test_unregister_notexists(self): registry = self._makeOne() func = lambda *x: None txn = Dummy() setattr(txn, registry.key, [None]) registry.unregister(func, txn) self.assertEqual(getattr(txn, registry.key), [None]) def test_unregister_funcs_is_None(self): registry = self._makeOne() func = lambda *x: None txn = Dummy() self.assertEqual(registry.unregister(func, txn), None) class UtilityFunctionTests(unittest.TestCase): def test_isActive(self): from repoze.tm import ekey from repoze.tm import isActive self.assertEqual(isActive({ekey:True}), True) self.assertEqual(isActive({}), False) class TestMakeTM(unittest.TestCase): def test_make_tm_withveto(self): from repoze.tm import make_tm tm = make_tm(DummyApplication(), {}, 'repoze.tm.tests:fakeveto') self.assertEqual(tm.commit_veto, fakeveto) def test_make_tm_noveto(self): from repoze.tm import make_tm tm = make_tm(DummyApplication(), {}, None) self.assertEqual(tm.commit_veto, None) class Test_default_commit_veto(unittest.TestCase): def _callFUT(self, status, headers=()): from repoze.tm import default_commit_veto return default_commit_veto(None, status, headers) def test_it_true_5XX(self): self.assertTrue(self._callFUT('500 Server Error')) self.assertTrue(self._callFUT('503 Service Unavailable')) def test_it_true_4XX(self): self.assertTrue(self._callFUT('400 Bad Request')) self.assertTrue(self._callFUT('411 Length Required')) def test_it_false_2XX(self): self.assertFalse(self._callFUT('200 OK')) self.assertFalse(self._callFUT('201 Created')) def test_it_false_3XX(self): self.assertFalse(self._callFUT('301 Moved Permanently')) self.assertFalse(self._callFUT('302 Found')) def test_it_true_x_tm_abort_specific(self): self.assertTrue(self._callFUT('200 OK', [('X-Tm-Abort', True)])) def test_it_false_x_tm_commit(self): self.assertFalse(self._callFUT('200 OK', [('X-Tm', 'commit')])) def test_it_true_x_tm_abort(self): self.assertTrue(self._callFUT('200 OK', [('X-Tm', 'abort')])) def test_it_true_x_tm_anythingelse(self): self.assertTrue(self._callFUT('200 OK', [('X-Tm', '')])) def test_x_tm_generic_precedes_x_tm_abort_specific(self): self.assertFalse(self._callFUT('200 OK', [('X-Tm', 'commit'), ('X-Tm-Abort', True)])) def fakeveto(environ, status, headers): """ """ class DummyTransactionModule: begun = False committed = False aborted = False def __init__(self, doom=False): self.doom = doom def begin(self): self.begun = True def get(self): return self def commit(self): self.committed = True def abort(self): self.aborted = True def isDoomed(self): return self.doom class Dummy: pass class DummyApplication: def __init__(self, exception=False, status="200 OK"): self.exception = exception self.status = status def __call__(self, environ, start_response): start_response(self.status, [], None) if self.exception: raise ValueError('raising') return ['hello'] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1673913305.5942993 repoze.tm2-2.2.0/repoze.tm2.egg-info/0000775000175000017500000000000000000000000020366 5ustar00tseavertseaver00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673913305.0 repoze.tm2-2.2.0/repoze.tm2.egg-info/PKG-INFO0000644000175000017500000002253000000000000021463 0ustar00tseavertseaver00000000000000Metadata-Version: 2.1 Name: repoze.tm2 Version: 2.2.0 Summary: Per-request transactions via WSGI middleware Home-page: http://www.repoze.org Author: Agendaless Consulting Author-email: repoze-dev@lists.repoze.org License: BSD-derived (http://www.repoze.org/LICENSE.txt) Description: repoze.tm2 (Transaction Manager) ================================ .. image:: https://travis-ci.org/repoze/repoze.tm2.png?branch=master :target: https://travis-ci.org/repoze/repoze.tm2 .. image:: https://readthedocs.org/projects/repozetm2/badge/?version=latest :target: http://repozetm2.readthedocs.org/en/latest/ :alt: Documentation Status .. image:: https://img.shields.io/pypi/v/repoze.tm2.svg :target: https://pypi.python.org/pypi/repoze.tm2 .. image:: https://img.shields.io/pypi/pyversions/repoze.tm2.svg :target: https://pypi.python.org/pypi/repoze.tm2 Middleware which uses the ZODB transaction manager to wrap a call to its pipeline children inside a transaction. This is a fork of the ``repoze.tm`` package which depends only on the ``transaction`` package rather than the entirety of ZODB (for users who don't rely on ZODB). Installation ------------ Install using setuptools, e.g. (within a virtualenv):: $ easy_install repoze.tm2 or using pip:: $ pip install repoze.tm2 Usage ----- For details on using the various components, please see the documentation in ``docs/index.rst``. A rendered version of that documentation is also available online: - http://repozetm2.readthedocs.org/en/latest/ Reporting Bugs -------------- Please report bugs in this package to https://github.com/repoze/repoze.tm2/issues Obtaining Source Code --------------------- Download development or tagged versions of the software by visiting: https://github.com/repoze/repoze.tm2 Changelog ========= 2.2.0 (2023-01-16) ------------------ - Add support for Python 3.7, 3.8, 3.9, 3.10, and 3.11. - Drop suppport for Python 2.7, 3.4, 3.5 and 3.6. - Add Github Actions workflow running unit tests / coverage for PRs. 2.1 (2016-06-03) ---------------- - Add support for Python 3.4, 3.5 and PyPy3. - Drop support for Python 2.6 and 3.2. - Add support for testing under Travis. 2.0 (2013-06-26) ---------------- - Avoid swallowing the original exception while aborting the transaction in middleware. See PR #3. 2.0b1 (2013-04-05) ------------------ - Middleware is now a generator, to deal appropriately with application iterators which are themselves not lists. - Convert use of deprecated failIf/failUnless to assertFalse/assertTrue. - Add support for testing under supported Pythons using Tox. - Add explicit support for Python 3.2 ad 3.3. - Drop support for Python 2.4, 2.5. 1.0 (2012-03-24) ---------------- - Run OOTB under Python 2.4 / 2.5 (pin 'transaction' dependency to a supported version when running under 2.4 / 2.5). 1.0b2 (2011-07-18) ------------------ - A new header ``X-Tm`` is now honored by the ``default_commit_veto`` commit veto hook. If this header exists in the headerlist, its value must be a string. If its value is ``commit``, the transaction will be committed regardless of the status code or the value of ``X-Tm-Abort``. If the value of the ``X-Tm`` header is ``abort`` (or any other string value except ``commit``), the transaction will be aborted regardless of the status code or the value of ``X-Tm-Abort``. - Use of the ``X-Tm-Abort`` header is now deprecated. Instead use the ``X-Tm`` header with a value of ``abort`` instead. - Add API docs section. 1.0b1 (2011-01-19) ------------------ - Added ``repoze.tm.default_commit_veto`` commit veto hook. This commit veto hook aborts for 4XX and 5XX response codes, or if there's a header named ``X-Tm-Abort`` in the headerlist and allows a commit otherwise. - Documented commit veto hook. 1.0a5 (2009-09-07) ------------------ - Don't commit after aborting if the transaction was doomed or if the commit veto aborted. - Don't use "real" transaction module in tests. - 100% test coverage. 1.0a4 (2009-01-06) ------------------ - RESTify CHANGES, move docs in README.txt into Sphinx. - Remove ``setup.cfg`` (all dependencies available via PyPI). - Synchronization point with ``repoze.tm`` (0.9). 1.0a3 (2008-08-03) ------------------ Allow ``commit_veto`` hook to be specified within Paste config, ala:: [filter:tm] use = repoze.tm:make_tm commit_veto = some.package:myfunction ``myfunction`` should take three args: environ, status, headers and should return True if the txn should be aborted, False if it should be committed. Initial PyPI release. 1.0a2 (2008-07-15) ------------------ - Provide "commit_veto" hook point (contributed by Alberto Valverde). - Point easy_install at http://dist.repoze.org/tm2/dev/simple via setup.cfg. 1.0a1 (2008-01-09) ------------------ - Fork point: we've created repoze.tm2, which is repoze.tm that has a dependency only on the 'transaction' package instead of all of ZODB. - Better documentation for non-Zope usage in README.txt. 0.8 (2007-10-11) ---------------- - Relaxed requirement for ZODB 3.7.2, since we might need to use the package with other verions. Note that the tests which depend on transaction having "doom" semantics don't work with 3.7.2, anyway. 0.7 (2007-09-25) ---------------- - Depend on PyPI release of ZODB 3.7.2. Upgrade to this by doing bin/easy_install -U 'ZODB3 >= 3.7.1, < 3.8.0a' if necessary. 0.6 (2007-09-21) ---------------- - after_end.register and after_end.unregister must now be passed a transaction object rather than a WSGI environment to avoid the possibility that the WSGI environment used by a child participating in transaction management won't be the same one used by the repoze.tm package. - repoze.tm now inserts a key into the WSGI environment (``repoze.tm.active``) if it's active in the WSGI pipeline. An API function, repoze.tm:isActive can be called with a single argument, the WSGI environment, to check if the middleware is active. 0.5 (2007-09-18) ---------------- - Depend on rerolled ZODB 3.7.1 instead of zopelib. - Add license and copyright, change trove classifiers. 0.4 (2007-09-17) ---------------- - Depend on zopelib rather than ZODB 3.8.0b3 distribution, because the ZODB distribution pulls in various packages (zope.interface and ZEO most notably) that are incompatible with stock Zope 2.10.4 apps and older sandboxes. We'll need to revisit this. 0.3 (2007-09-14) ---------------- - Provide limited compatibility for older transaction package versions which don't support the 'transaction.isDoomed' API. 0.2 (2007-09-13) ---------------- - Provide after_end API for registering callbacks at transaction end. 0.1 (2007-09-10) ---------------- - Initial Release Keywords: web application server wsgi zope repoze Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Internet :: WWW/HTTP :: WSGI Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware Provides-Extra: docs ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673913305.0 repoze.tm2-2.2.0/repoze.tm2.egg-info/SOURCES.txt0000644000175000017500000000107700000000000022255 0ustar00tseavertseaver00000000000000.gitignore CHANGES.rst CONTRIBUTORS.txt COPYRIGHT.txt LICENSE.txt README.rst TODO.txt setup.cfg setup.py tox.ini docs/Makefile docs/api.rst docs/changes.rst docs/conf.py docs/index.rst docs/_static/logo_hi.png docs/_static/repoze.css repoze/__init__.py repoze.tm2.egg-info/PKG-INFO repoze.tm2.egg-info/SOURCES.txt repoze.tm2.egg-info/dependency_links.txt repoze.tm2.egg-info/entry_points.txt repoze.tm2.egg-info/namespace_packages.txt repoze.tm2.egg-info/not-zip-safe repoze.tm2.egg-info/requires.txt repoze.tm2.egg-info/top_level.txt repoze/tm/__init__.py repoze/tm/tests.py././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673913305.0 repoze.tm2-2.2.0/repoze.tm2.egg-info/dependency_links.txt0000644000175000017500000000000100000000000024432 0ustar00tseavertseaver00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673913305.0 repoze.tm2-2.2.0/repoze.tm2.egg-info/entry_points.txt0000644000175000017500000000010500000000000023656 0ustar00tseavertseaver00000000000000 [paste.filter_app_factory] tm = repoze.tm:make_tm ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673913305.0 repoze.tm2-2.2.0/repoze.tm2.egg-info/namespace_packages.txt0000644000175000017500000000000700000000000024714 0ustar00tseavertseaver00000000000000repoze ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1332613690.0 repoze.tm2-2.2.0/repoze.tm2.egg-info/not-zip-safe0000644000175000017500000000000100000000000022612 0ustar00tseavertseaver00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673913305.0 repoze.tm2-2.2.0/repoze.tm2.egg-info/requires.txt0000644000175000017500000000004700000000000022765 0ustar00tseavertseaver00000000000000transaction [docs] Sphinx transaction ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673913305.0 repoze.tm2-2.2.0/repoze.tm2.egg-info/top_level.txt0000644000175000017500000000000700000000000023113 0ustar00tseavertseaver00000000000000repoze ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1673913305.5942993 repoze.tm2-2.2.0/setup.cfg0000644000175000017500000000036200000000000016507 0ustar00tseavertseaver00000000000000[nosetests] match = ^test where = repoze/tm nocapture = 1 cover-package = repoze.tm cover-erase = 1 [aliases] dev = develop easy_install repoze.tm2[testing] docs = develop easy_install repoze.tm2[docs] [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673913267.0 repoze.tm2-2.2.0/setup.py0000664000175000017500000000374100000000000016406 0ustar00tseavertseaver00000000000000import os from setuptools import find_packages from setuptools import setup install_requires = ['transaction'] docs_extras = install_requires + ['Sphinx'] here = os.path.abspath(os.path.dirname(__file__)) def _read_file(filename): try: with open(os.path.join(here, filename)) as f: return f.read() except IOError: # Travis??? return '' README = _read_file('README.rst') CHANGES = _read_file('CHANGES.rst') setup(name='repoze.tm2', version='2.2.0', description='Per-request transactions via WSGI middleware', long_description=README + "\n\n" + CHANGES, classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI", "Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware", ], keywords='web application server wsgi zope repoze', author="Agendaless Consulting", author_email="repoze-dev@lists.repoze.org", url="http://www.repoze.org", license="BSD-derived (http://www.repoze.org/LICENSE.txt)", packages=find_packages(), include_package_data=True, namespace_packages=['repoze'], zip_safe=False, install_requires=install_requires, tests_require=install_requires, test_suite = "repoze.tm.tests", entry_points=""" [paste.filter_app_factory] tm = repoze.tm:make_tm """, extras_require = { 'docs': docs_extras, } ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1673912333.0 repoze.tm2-2.2.0/tox.ini0000664000175000017500000000156400000000000016210 0ustar00tseavertseaver00000000000000[tox] envlist = py37,py38,py39,py310,py311,pypy3,cover,docs [testenv] commands = python -m pytest --cov=repoze.tm --cov-append --cov-report= {toxinidir}/repoze/tm/tests.py usedevelop = true deps = zope.interface transaction pytest pytest-cov setenv = COVERAGE_FILE=.coverage.{envname} [testenv:cover] skip_install = true basepython = python3.10 commands = coverage combine coverage report --fail-under=100 --show-missing deps = coverage setenv = COVERAGE_FILE=.coverage # we separate coverage into its own testenv because a) "last run wins" wrt # cobertura jenkins reporting and b) pypy and jython can't handle any # combination of versions of coverage and nosexcover that i can find. [testenv:docs] basepython = python3.10 commands = sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html deps = Sphinx