├── .coveragerc
├── .editorconfig
├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE.txt
├── MANIFEST.in
├── README.rst
├── changes.rst
├── docs
├── Makefile
├── changes.rst
├── conf.py
├── index.rst
├── init.rst
├── tests.rst
├── transactions.rst
└── usage.rst
├── setup.cfg
├── setup.py
├── src
└── pyramid_sqlalchemy
│ ├── __init__.py
│ ├── events.py
│ ├── fixtures.py
│ ├── meta.py
│ ├── model.py
│ └── testing.py
└── tests
├── __init__.py
├── test_init.py
└── test_testing.py
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | branch = true
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | trim_trailing_whitespace = true
3 | charset = utf-8
4 |
5 | [**.py]
6 | indent_size = 4
7 | indent_style = space
8 |
9 | [Makefile]
10 | indent_size = 8
11 | indent_style = tab
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sw[opq]
2 | *.py[co]
3 | /.cache
4 | /.coverage
5 | /.pytest_cache
6 | /build
7 | /dist
8 | /coverage.xml
9 | /nosetests.xml
10 | /docs/_build
11 | /src/pyramid_sqlalchemy.egg-info
12 |
13 | /.Python
14 | /bin
15 | /include
16 | /lib
17 |
18 | /pyvenv.cfg
19 | /pip-selfcheck.json
20 |
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.7"
4 | - "3.5"
5 | - "3.6"
6 | - "3.7"
7 | - "3.8"
8 | - pypy
9 | - pypy3
10 | install:
11 | - pip install -e '.[tests]'
12 | script: py.test
13 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Reporting an issue
2 | ------------------
3 |
4 | When submitting a ticket please include the following information:
5 |
6 | * the installed version of `pyramid_sqlalchemy`
7 | * the installed version of `pyramid`
8 | * the installed version of `SQLAlchemy`
9 | * if you encounter an exception: the complete Python backtrace
10 |
11 |
12 | Patches and pull requests
13 | -------------------------
14 |
15 | When submitting a patch or pull request please make sure it applies cleanly to
16 | the current git master branch and all tests are passing on Python 2.7 and
17 | Python 3.4. To easiest way to run the tests is to use pytest in a virtualenv.
18 | For Python 2 use the following:
19 |
20 | ```
21 | $ virtualenv .
22 | $ bin/pip install -e '.[tests]'
23 | $ bin/pip install pytest
24 | $ bin/py.test src
25 | ```
26 |
27 | For Python 3.4 use the following:
28 |
29 | ```
30 | $ pythonn3.4 -m venv .
31 | $ bin/pip install -e '.[tests]'
32 | $ bin/pip install pytest
33 | $ bin/py.test src
34 | ```
35 |
36 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) Wichert Akkerman
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. Neither the name of the University nor the names of its contributors
13 | may be used to endorse or promote products derived from this software
14 | without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 | SUCH DAMAGE.
27 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.rst
2 | include *.txt
3 | include *.md
4 | include .travis.yml
5 | graft docs
6 | prune docs/_build
7 |
8 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | .. image:: https://travis-ci.org/wichert/pyramid_sqlalchemy.svg?branch=master
2 | :target: https://travis-ci.org/wichert/pyramid_sqlalchemy
3 |
4 | `pyramid_sqlalchemy` provides some basic glue to facilitate using
5 | `SQLAlchemy `_ with `Pyramid
6 | `_.
7 |
8 | SQLAlchemy relies on global state for a few things:
9 |
10 | * A ``MetaData`` instance which tracks all known SQL tables.
11 | * A base class for all models using the ORM.
12 | * A session factory.
13 |
14 | Every application using SQLAlchemy must provides its own instance of these.
15 | This makes it hard create add-on packages that also use SQLAlchemy, since they
16 | either need to have their own SQLAlchemy state, which makes it hard to
17 | integrate them into your application, or they need to jump through multiple
18 | complex hoops to allow them share state with your application.
19 |
20 | pyramid_sqlalchemy helps by providing a canonical location for the global
21 | SQLAlchemy state. In addition it provides a convenient way to configure
22 | SQLAlchemy in a Pyramid application.
23 |
24 | ::
25 |
26 | from pyramid.config import Configurator
27 | from pyramid_sqlalchemy import BaseObject
28 |
29 | class MyModel(BaseObject):
30 | __tablename__ = 'my_model'
31 | ...
32 |
33 | def main():
34 | config = Configurator()
35 | # Configure SQLAlchemy using settings from the .ini file
36 | config.include('pyramid_sqlalchemy')
37 | ...
38 | return config.make_wsgi_app()
39 |
--------------------------------------------------------------------------------
/changes.rst:
--------------------------------------------------------------------------------
1 | docs/changes.rst
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = ../bin/sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 |
15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
16 |
17 | help:
18 | @echo "Please use \`make ' where is one of"
19 | @echo " html to make standalone HTML files"
20 | @echo " dirhtml to make HTML files named index.html in directories"
21 | @echo " singlehtml to make a single large HTML file"
22 | @echo " pickle to make pickle files"
23 | @echo " json to make JSON files"
24 | @echo " htmlhelp to make HTML files and a HTML help project"
25 | @echo " qthelp to make HTML files and a qthelp project"
26 | @echo " devhelp to make HTML files and a Devhelp project"
27 | @echo " epub to make an epub"
28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
29 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
30 | @echo " text to make text files"
31 | @echo " man to make manual pages"
32 | @echo " changes to make an overview of all changed/added/deprecated items"
33 | @echo " linkcheck to check all external links for integrity"
34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
35 |
36 | clean:
37 | -rm -rf $(BUILDDIR)/*
38 |
39 | html:
40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
41 | @echo
42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
43 |
44 | dirhtml:
45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
48 |
49 | singlehtml:
50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
51 | @echo
52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
53 |
54 | pickle:
55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
56 | @echo
57 | @echo "Build finished; now you can process the pickle files."
58 |
59 | json:
60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
61 | @echo
62 | @echo "Build finished; now you can process the JSON files."
63 |
64 | htmlhelp:
65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
66 | @echo
67 | @echo "Build finished; now you can run HTML Help Workshop with the" \
68 | ".hhp project file in $(BUILDDIR)/htmlhelp."
69 |
70 | qthelp:
71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
72 | @echo
73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/s4usqlalchemy.qhcp"
76 | @echo "To view the help file:"
77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/s4usqlalchemy.qhc"
78 |
79 | devhelp:
80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
81 | @echo
82 | @echo "Build finished."
83 | @echo "To view the help file:"
84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/s4usqlalchemy"
85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/s4usqlalchemy"
86 | @echo "# devhelp"
87 |
88 | epub:
89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
90 | @echo
91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
92 |
93 | latex:
94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
95 | @echo
96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
98 | "(use \`make latexpdf' here to do that automatically)."
99 |
100 | latexpdf:
101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
102 | @echo "Running LaTeX files through pdflatex..."
103 | make -C $(BUILDDIR)/latex all-pdf
104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
105 |
106 | text:
107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
108 | @echo
109 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
110 |
111 | man:
112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
113 | @echo
114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
115 |
116 | changes:
117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
118 | @echo
119 | @echo "The overview file is in $(BUILDDIR)/changes."
120 |
121 | linkcheck:
122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
123 | @echo
124 | @echo "Link check complete; look for any errors in the above output " \
125 | "or in $(BUILDDIR)/linkcheck/output.txt."
126 |
127 | doctest:
128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
129 | @echo "Testing of doctests in the sources finished, look at the " \
130 | "results in $(BUILDDIR)/doctest/output.txt."
131 |
--------------------------------------------------------------------------------
/docs/changes.rst:
--------------------------------------------------------------------------------
1 | Changelog
2 | =========
3 |
4 | 1.6 - January 4, 2016
5 | ---------------------
6 |
7 | - Update ``transaction`` pytest fixture to not mock out ``transation.get()``
8 | completely, but only the ``commit()`` transaction method. This fixes problems
9 | with code trying to write on the current transaction.
10 |
11 |
12 | 1.5 - December 30, 2015
13 | -----------------------
14 |
15 | - Fix a compatibility error with the DatabaseTestCase class which could break
16 | functional test setup.
17 |
18 | - Code reorganisation: move tests outside the package; there is no point in
19 | installing them.
20 |
21 |
22 | 1.4 - November 25, 2015
23 | -----------------------
24 |
25 | - Revert naming convention change. This change broke all existing data models
26 | which did not supply a constraint name everywhere. This is especially bad
27 | for types which implicitly create unnamed constraints, such as booleans and
28 | enums on dialects that do not have native support.
29 |
30 |
31 | 1.3 - November 23, 2015
32 | -----------------------
33 |
34 | - Configure a default naming convention, as `recommended by alembic
35 | `_).
36 | `Pull request 3 `_
37 | from Marcin Lulek.
38 |
39 | - Fix a broken import in pyramid_sqlalchemy's own test when running on Python 3.
40 |
41 | - Allow overriding the database used for testing with the pytest ``--sql-url``
42 | option when using the ``DatabaseTestCase`` test class. For non-pytest users
43 | support the ``DB_URI`` environment variable as well.
44 |
45 |
46 | 1.2.2 - September 11, 2014
47 | --------------------------
48 |
49 | - Add dependency on mock for Python <3.3. This fixes import problems who try to
50 | import pyramid_sqlalchemy.testing in production code.
51 |
52 |
53 | 1.2.1 - September 1, 2014
54 | -------------------------
55 |
56 | - Move pyramid to a test-only dependency. This makes it simpler to use
57 | pyramid_sqlalchemy in non-pyramid contexts.
58 |
59 |
60 | 1.2 - August 30, 2014
61 | ---------------------
62 |
63 | - Use `unittest.mock` when available. This removes the `mock` dependency on
64 | Python 3.
65 |
66 | - Tests no longer need to mock out pyramid_sqlalchemy.includeme; this is now
67 | handled by ``DatabaseTestCase`` and the py.test fixtures.
68 |
69 | - Automatically make py.test fixtures available externally. This removes the
70 | need to copy & paste them over from the documentation.
71 |
72 | - Fix error on pytest fixture example.
73 |
74 | - Setup `Travis `_ to
75 | automatically run tests on CPython 2.6, CPython 2.7, CPython 3.3, CPython 3.4
76 | and PyPy.
77 |
78 |
79 | 1.1 - July 14, 2014
80 | -------------------
81 |
82 | - Add missing schema to the Pyramid-URL in the package description. This broke
83 | ReST rendering on the PyPI page.
84 |
85 | - Add a new ``enable_sql_two_phase_commit()`` configuration directive to enable
86 | two-phase commit.
87 |
88 | - Enable foreign key constraint checking for SQLite in DatabaseTestCase.
89 |
90 | - Use SQLAlchemy events instead of ZopeTransactionExtension to handle
91 | integration of zope.sqlalchemy and SQLAlchemy.
92 |
93 |
94 | 1.0 - July 13, 2014
95 | -------------------
96 |
97 | - First release.
98 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # pyramid_sqlalchemy documentation build configuration file, created by
4 | # sphinx-quickstart on Tue Jul 12 17:42:20 2011.
5 | #
6 | # This file is execfile()d with the current directory set to its containing dir.
7 | #
8 | # Note that not all possible configuration values are present in this
9 | # autogenerated file.
10 | #
11 | # All configuration values have a default; values that are commented out
12 | # serve to show the default.
13 |
14 |
15 | # If extensions (or modules to document with autodoc) are in another directory,
16 | # add these directories to sys.path here. If the directory is relative to the
17 | # documentation root, use os.path.abspath to make it absolute, like shown here.
18 | #sys.path.insert(0, os.path.abspath('.'))
19 |
20 | # -- General configuration -----------------------------------------------------
21 |
22 | # If your documentation needs a minimal Sphinx version, state it here.
23 | #needs_sphinx = '1.0'
24 |
25 | # Add any Sphinx extension module names here, as strings. They can be extensions
26 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
27 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
28 |
29 | # Add any paths that contain templates here, relative to this directory.
30 | templates_path = ['_templates']
31 |
32 | # The suffix of source filenames.
33 | source_suffix = '.rst'
34 |
35 | # The encoding of source files.
36 | #source_encoding = 'utf-8-sig'
37 |
38 | # The master toctree document.
39 | master_doc = 'index'
40 |
41 | # General information about the project.
42 | project = u'pyramid_sqlalchemy'
43 | copyright = u'2011-2014, Wichert Akkerman'
44 |
45 | # The version info for the project you're documenting, acts as replacement for
46 | # |version| and |release|, also used in various other places throughout the
47 | # built documents.
48 | #
49 | # The short X.Y version.
50 | version = '1.2'
51 | # The full version, including alpha/beta/rc tags.
52 | release = '1.2'
53 |
54 | # The language for content autogenerated by Sphinx. Refer to documentation
55 | # for a list of supported languages.
56 | #language = None
57 |
58 | # There are two options for replacing |today|: either, you set today to some
59 | # non-false value, then it is used:
60 | #today = ''
61 | # Else, today_fmt is used as the format for a strftime call.
62 | #today_fmt = '%B %d, %Y'
63 |
64 | # List of patterns, relative to source directory, that match files and
65 | # directories to ignore when looking for source files.
66 | exclude_patterns = ['_build']
67 |
68 | # The reST default role (used for this markup: `text`) to use for all documents.
69 | #default_role = None
70 |
71 | # If true, '()' will be appended to :func: etc. cross-reference text.
72 | #add_function_parentheses = True
73 |
74 | # If true, the current module name will be prepended to all description
75 | # unit titles (such as .. function::).
76 | #add_module_names = True
77 |
78 | # If true, sectionauthor and moduleauthor directives will be shown in the
79 | # output. They are ignored by default.
80 | #show_authors = False
81 |
82 | # The name of the Pygments (syntax highlighting) style to use.
83 | pygments_style = 'sphinx'
84 |
85 | # A list of ignored prefixes for module index sorting.
86 | #modindex_common_prefix = []
87 |
88 |
89 | # -- Options for HTML output ---------------------------------------------------
90 |
91 | # The theme to use for HTML and HTML Help pages. See the documentation for
92 | # a list of builtin themes.
93 | html_theme = 'haiku'
94 |
95 | # Theme options are theme-specific and customize the look and feel of a theme
96 | # further. For a list of options available for each theme, see the
97 | # documentation.
98 | #html_theme_options = {}
99 |
100 | # Add any paths that contain custom themes here, relative to this directory.
101 | #html_theme_path = []
102 |
103 | # The name for this set of Sphinx documents. If None, it defaults to
104 | # " v documentation".
105 | html_title = 'pyramid_sqlalchemy 1.0'
106 |
107 | # A shorter title for the navigation bar. Default is the same as html_title.
108 | #html_short_title = None
109 |
110 | # The name of an image file (relative to this directory) to place at the top
111 | # of the sidebar.
112 | #html_logo = None
113 |
114 | # The name of an image file (within the static path) to use as favicon of the
115 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
116 | # pixels large.
117 | #html_favicon = None
118 |
119 | # Add any paths that contain custom static files (such as style sheets) here,
120 | # relative to this directory. They are copied after the builtin static files,
121 | # so a file named "default.css" will overwrite the builtin "default.css".
122 | html_static_path = ['_static']
123 |
124 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
125 | # using the given strftime format.
126 | #html_last_updated_fmt = '%b %d, %Y'
127 |
128 | # If true, SmartyPants will be used to convert quotes and dashes to
129 | # typographically correct entities.
130 | #html_use_smartypants = True
131 |
132 | # Custom sidebar templates, maps document names to template names.
133 | #html_sidebars = {}
134 |
135 | # Additional templates that should be rendered to pages, maps page names to
136 | # template names.
137 | #html_additional_pages = {}
138 |
139 | # If false, no module index is generated.
140 | #html_domain_indices = True
141 |
142 | # If false, no index is generated.
143 | #html_use_index = True
144 |
145 | # If true, the index is split into individual pages for each letter.
146 | #html_split_index = False
147 |
148 | # If true, links to the reST sources are added to the pages.
149 | #html_show_sourcelink = True
150 |
151 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
152 | #html_show_sphinx = True
153 |
154 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
155 | #html_show_copyright = True
156 |
157 | # If true, an OpenSearch description file will be output, and all pages will
158 | # contain a tag referring to it. The value of this option must be the
159 | # base URL from which the finished HTML is served.
160 | #html_use_opensearch = ''
161 |
162 | # This is the file name suffix for HTML files (e.g. ".xhtml").
163 | #html_file_suffix = None
164 |
165 | # Output file base name for HTML help builder.
166 | htmlhelp_basename = 'pyramid_sqlalchemydoc'
167 |
168 |
169 | # -- Options for LaTeX output --------------------------------------------------
170 |
171 | # The paper size ('letter' or 'a4').
172 | #latex_paper_size = 'letter'
173 |
174 | # The font size ('10pt', '11pt' or '12pt').
175 | #latex_font_size = '10pt'
176 |
177 | # Grouping the document tree into LaTeX files. List of tuples
178 | # (source start file, target name, title, author, documentclass [howto/manual]).
179 | latex_documents = [
180 | ('index', 'pyramid_sqlalchemy', u'pyramid_sqlalchemy Documentation',
181 | u'Wichert Akkerman', 'manual'),
182 | ]
183 |
184 | # The name of an image file (relative to this directory) to place at the top of
185 | # the title page.
186 | #latex_logo = None
187 |
188 | # For "manual" documents, if this is true, then toplevel headings are parts,
189 | # not chapters.
190 | #latex_use_parts = False
191 |
192 | # If true, show page references after internal links.
193 | #latex_show_pagerefs = False
194 |
195 | # If true, show URL addresses after external links.
196 | #latex_show_urls = False
197 |
198 | # Additional stuff for the LaTeX preamble.
199 | #latex_preamble = ''
200 |
201 | # Documents to append as an appendix to all manuals.
202 | #latex_appendices = []
203 |
204 | # If false, no module index is generated.
205 | #latex_domain_indices = True
206 |
207 |
208 | # -- Options for manual page output --------------------------------------------
209 |
210 | # One entry per manual page. List of tuples
211 | # (source start file, name, description, authors, manual section).
212 | man_pages = [
213 | ('index', 'pyramid_sqlalchemy', u'pyramid_sqlalchemy Documentation',
214 | [u'Wichert Akkerman'], 1)
215 | ]
216 |
217 |
218 | # Example configuration for intersphinx: refer to the Python standard library.
219 | intersphinx_mapping = {
220 | 'python': ('http://docs.python.org/', None),
221 | 'pyramid': ('http://docs.pylonsproject.org/projects/pyramid/en/latest/', None),
222 | 'sqlalchemy': ('http://www.sqlalchemy.org/docs', None)}
223 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | `pyramid_sqlalchemy` provides some basic glue to facilitate using
2 | `SQLAlchemy `_ with `Pyramid
3 | `_.
4 |
5 | SQLAlchemy relies on global state for a few things:
6 |
7 | * A :ref:`MetaData ` instance which tracks all
8 | known SQL tables.
9 | * A :ref:`ORM base class ` for all models using the ORM.
10 | * A :ref:`session factory `.
11 |
12 | Every application using SQLAlchemy must provides its own instance of these.
13 | This makes it hard create add-on packages that also use SQLAlchemy, since they
14 | either need to have their own SQLAlchemy state, which makes it hard to
15 | integrate them into your application, or they need to jump through multiple
16 | complex hoops to allow them share state with your application.
17 |
18 | pyramid_sqlalchemy helps by providing a canonical location for the global
19 | SQLAlchemy state. In addition it provides a convenient way to configure
20 | SQLAlchemy in a Pyramid application.
21 |
22 | .. code-block:: python
23 | :linenos:
24 |
25 | from pyramid.config import Configurator
26 | from pyramid_sqlalchemy import BaseObject
27 |
28 | class MyModel(BaseObject):
29 | __tablename__ = 'my_model'
30 | ...
31 |
32 | def main(global_config, **settings):
33 | config = Configurator(settings=settings)
34 | # Configure SQLAlchemy using settings from the .ini file
35 | config.include('pyramid_sqlalchemy')
36 | ...
37 | return config.make_wsgi_app()
38 |
39 |
40 | Contents
41 | ========
42 |
43 | .. toctree::
44 | :maxdepth: 1
45 |
46 | usage
47 | init
48 | transactions
49 | tests
50 | changes
51 |
52 | Indices and tables
53 | ==================
54 |
55 | * :ref:`genindex`
56 | * :ref:`modindex`
57 | * :ref:`search`
58 |
--------------------------------------------------------------------------------
/docs/init.rst:
--------------------------------------------------------------------------------
1 | Initialisation
2 | ==============
3 |
4 | When using pyramid_sqlalchemy in a Pyramid application you can easily configure
5 | it by using the ``includeme`` function of the Pyramid configurator object:
6 |
7 | .. code-block:: python
8 | :linenos:
9 |
10 | from pyramid.config import Configurator
11 |
12 | def main(global_config, **settings):
13 | config = Configurator(settings=settings)
14 | # Configure SQLAlchemy using settings from the .ini file
15 | config.include('pyramid_sqlalchemy')
16 |
17 | This will pick up any ``sqlalchemy.*`` entries from your ``.ini`` file and
18 | use those to configure SQLAlchemy. In particular the ``sqlalchemy.url``
19 | entry is used, which must contain a :ref:`database URL
20 | `.
21 |
22 | For non-Pyramid applications or special situations you can also use
23 | :py:func:`pyramid_sqlalchemy.init_sqlalchemy` to configure a SQLAlchemy engine
24 | directly:
25 |
26 | .. code-block:: python
27 | :linenos:
28 |
29 | from sqlalchemy import create_engine
30 | from pyramid_sqlalchemy import init_sqlalchemy
31 |
32 | engine = create_engine('sqlite://')
33 | init_sqlachemy(engine)
34 |
35 |
36 | Two-phase transactions
37 | ----------------------
38 |
39 | If your application uses both SQL and other transaction-aware systems such as
40 | `repoze.filesafe `_, `AcidFS
41 | `_, `pyramid_mailer
42 | `_ or `ZODB `_
43 | you need to `two-phase commits
44 | `_ to coordinate
45 | transactions. You can enable these using the ``enable_sql_two_phase_commit``
46 | configuration directive.
47 |
48 | .. code-block:: python
49 | :linenos:
50 |
51 | def main(global_config, **settings):
52 | config = Configurator(settings=settings)
53 | config.include('pyramid_sqlalchemy')
54 | config.enable_sql_two_phase_commit()
55 |
56 | Please note that this is not supported for all SQL servers. PostgreSQL is
57 | the only server where this is guaranteed to work. SQL Server does support
58 | two-phase transactions but the Python driver support for it is unusable.
59 | The cx_oracle `supports them with some caveats
60 | `_.
61 |
--------------------------------------------------------------------------------
/docs/tests.rst:
--------------------------------------------------------------------------------
1 | Writing tests
2 | =============
3 |
4 | unittest test cases
5 | -------------------
6 |
7 | pyramid_sqlalchemy provides a ``DatabaseTestCase`` class which can be used when
8 | writing unit or integration tests that require a working database. You can use
9 | this as base class for you test classes. This example updates the
10 | :ref:`Pyramid integration test example ` to add
11 | database support.
12 |
13 |
14 | .. code-block:: python
15 | :linenos:
16 |
17 |
18 | from pyramid_sqlalchemy.testing import DatabaseTestCase
19 | from pyramid import testing
20 |
21 | class ViewIntegrationTests(DatabaseTestCase):
22 | def setUp(self):
23 | super(ViewIntegrationTests, self).setUp()
24 | self.config = testing.setUp()
25 | self.config.include('myapp')
26 |
27 | def tearDown(self):
28 | testing.tearDown()
29 | super(ViewIntegrationTests, self).tearDown()
30 |
31 |
32 | .. warning::
33 |
34 | It is critical that you call the setUp() method of the base classes first.
35 | This is necessary to guarantee everything is configured correctly before you
36 | run any code that might touch pyramid_sqlalchemy.
37 |
38 | Writing functional tests is just as easy. The example below is a modified
39 | version of the :ref:`Pyramid functional test example
40 | `.
41 |
42 | .. code-block:: python
43 | :linenos:
44 |
45 | from pyramid_sqlalchemy.testing import DatabaseTestCase
46 |
47 | class FunctionalTests(DatabaseTestCase):
48 | def setUp(self):
49 | from myapp import main
50 | from webtest import TestApp
51 | super(FunctionalTests, self).setUp()
52 | app = main({})
53 | self.testapp = TestApp(app)
54 |
55 |
56 |
57 | Normally all tests are run with an in-memory SQLite database. There are several
58 | ways to change this:
59 |
60 | * Set the ``db_uri`` variable in your test class to a different database URI.
61 | * Set the ``DB_URI`` environment variable.
62 | * If you use pytest as test runner you can use the ``--sql-url`` option to
63 | set the database URI.
64 |
65 |
66 | py.test fixtures
67 | ----------------
68 |
69 | If you use `pytest `_ you can use the test fixtures provided
70 | by pyramid_sqlalchemy.
71 |
72 | For tests that need an active connection, but that do not need to use SQLAlchemy
73 | there is a ``transaction`` fixture available. This fixture creates a new transaction
74 | that will automatically be aborted at the end of the test. In order to prevent the
75 | transaction from being committed accidentally it is marked as `doomed`: this will
76 | turn any call to ``commit()`` into an error.
77 |
78 | .. code-block:: python
79 | :linenos:
80 |
81 | @pytest.mark.usefixtures('transaction')
82 | def test_transaction_integration():
83 | # Test code that needs a transaction
84 |
85 | The ``sql_session`` fixture must be used to test any code that needs to access
86 | a database. This fixture will setup a SQL backend and create all known tables.
87 | To speed up tests this will only be done once for the py.test session. Each
88 | test itself is running within its own transaction, to guarantee that any
89 | database changes are reverted after the test, and the next test starts with a
90 | clean database.
91 |
92 | .. code-block:: python
93 | :linenos:
94 |
95 | def test_model_sets_id_automatically(sql_session):
96 | obj = Account(login='jane')
97 | sql_session.add(obj)
98 | sql_session.flush()
99 | assert obj.id is not None
100 |
101 | Normally all tests will use an in-memory SQLite database. You can run your tests
102 | with a different backend by using the ``--sql-url=`` commandline option. For
103 | example to run all tests against a local PostgreSQL server using the `pytest`
104 | database::
105 |
106 | $ bin/py.test --sql-url=postgresql:///pytest
107 |
108 | There is also a ``--sql-echo`` commandline option which will echo all executed SQL
109 | statements to the console. This must be used in combination with pytests' ``-s``
110 | option to make the console output visisble.
111 |
112 | ::
113 |
114 | $ bin/py.test --sql-echo -s
115 | ======================================= test session starts ========================================
116 | platform darwin -- Python 2.7.8 -- py-1.4.20 -- pytest-2.5.2
117 | plugins: pyramid-sqlalchemy
118 | collected 36 items / 3 skipped
119 |
120 | tests/ext/test_sql.py 2014-08-30 09:02:38,070 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
121 | 2014-08-30 09:02:38,070 INFO sqlalchemy.engine.base.Engine ()
122 | 2014-08-30 09:02:38,070 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
123 |
124 | Using the provided fixtures you can create a new fixture for functional tests.
125 | This fixture needs add a special key to the request environment to tell the
126 | `pyramid_tm` tween not to to create or commit transactions.
127 |
128 | .. code-block:: python
129 | :linenos:
130 |
131 | import pytest
132 | from webtest import TestApp
133 | from myapp import main
134 |
135 | @pytest.fixture
136 | def app(monkeypatch, sql_session):
137 | # Prevent SQL re-configuration with non-testing setup
138 | monkeypatch.setattr('pyramid_sqlalchemy.includeme', lambda c: None)
139 | app = main({})
140 | return TestApp(app, extra_environ={'repoze.tm.active': True})
141 |
142 |
--------------------------------------------------------------------------------
/docs/transactions.rst:
--------------------------------------------------------------------------------
1 | Transactions
2 | ============
3 |
4 | All database operations must use a transaction. Transactions are created
5 | automatically when you perform a SQL operation, but you must provide a way to
6 | commit a transaction. There are two ways to manage transaction: automatically
7 | for each request using pyramid_tm, or manually.
8 |
9 | Using pyramid_tm
10 | ----------------
11 |
12 | The easiest way to handle transactions is to use the `pyramid_tm
13 | `_ package.
14 |
15 | .. code-block:: python
16 | :linenos:
17 |
18 | def main():
19 | config = Configurator()
20 | config.include('pyramid_tm')
21 | config.include('pyramid_sqlalchemy')
22 |
23 | pyramid_tm will automatically commit the transaction when processing of a
24 | request has finished. If an exception is raised during request processing the
25 | transaction will be aborted. Note that raising HTTP exceptions such as
26 | HTTPFound will also abort the transaction. This is the main difference in
27 | Pyramid between raising a HTTP exception and returning it.
28 |
29 |
30 | Managing transactions manually
31 | ------------------------------
32 |
33 | You can also manage transactions manually. To do this you will need to use the
34 | API provided by the `transaction `_
35 | package. The easiest way to do this is to use its context manager.
36 |
37 | .. code-block:: python
38 | :linenos:
39 |
40 | import transaction
41 | from pyramid_sqlalchemy import Session
42 | from .model import MyModel
43 |
44 | def my_func():
45 | with transaction.manager:
46 | Session.query(MyModel).update({'active': False})
47 |
48 |
49 | The transaction manager will automatically commit the transaction, unless an
50 | exception is raised, in which case it will abort the transaction. You can also
51 | abort the transaction manually by calling ``abort()`` on the transaction.
52 |
53 | .. code-block:: python
54 | :linenos:
55 |
56 | import transaction
57 | from pyramid_sqlalchemy import Session
58 | from .model import MyModel
59 |
60 | def my_func():
61 | with transaction.manager as tx:
62 | Session.query(MyModel).update({'active': False})
63 | if something_bad_happened:
64 | tx.abort()
65 |
66 | If you prefer not to use the context manager you can also use the underlying
67 | transaction API directly. The methods you can use are:
68 |
69 | * ``transaction.abort()`` aborts the current transaction.
70 | * ``transaction.commit()`` commits the current transaction.
71 |
72 | .. code-block:: python
73 | :linenos:
74 |
75 | import transaction
76 | from pyramid_sqlalchemy import Session
77 | from .model import MyModel
78 |
79 | def my_func():
80 | Session.query(MyModel).update({'active': False})
81 | if something_bad_happened:
82 | tx.abort()
83 | else:
84 | tx.commit()
85 |
86 |
87 | Savepoints or nested transactions
88 | ---------------------------------
89 |
90 | .. caution::
91 |
92 | Not all SQL implementations support savepoints.
93 |
94 | Savepoints, sometimes also known as nested transactions, provide a way to
95 | execute code that might fail, but where a failure should not doom the rest of
96 | the transaction. The transaction system allows you to create savepoints for
97 | this purpose.
98 |
99 | .. code-block:: python
100 | :linenos:
101 |
102 | import transaction
103 |
104 | def my_func():
105 | ...
106 | savepoint = transaction.savepoint()
107 | try:
108 | dangerous_function()
109 | except:
110 | savepoint.rollback()
111 | ...
112 |
113 |
114 | If you manage transaction manually you should call the savepoint method of the
115 | current transaction instead of the global savepoint function.
116 |
117 | .. code-block:: python
118 | :linenos:
119 |
120 | import transaction
121 |
122 | with transaction.manager as tx:
123 | ...
124 | savepoint = tx.savepoint()
125 | try:
126 | dangerous_function()
127 | except:
128 | savepoint.rollback()
129 | ...
130 |
131 |
132 | Non-ORM modifications
133 | ---------------------
134 |
135 | zope.sqlalchemy, which is used to handle the integration of SQLAlchemy and
136 | the transaction system, can only detect changes made through the ORM. Sometimes
137 | you may need to bypass the ORM and execute SQL statements directly using SQLAlchemy's
138 | core API.
139 |
140 | .. code-block:: python
141 | :linenos:
142 |
143 | from pyramid_sqlalchemy import Session
144 | from myapp.models import MyModel
145 |
146 | # Execute an UPDATE query directly, without using the ORM
147 | Session.query(MyModel).update({'active': False})
148 |
149 | If you do this zope.sqlalchemy will not detect that you made any changes and
150 | will not correctly commit the transaction. To handle this you must call
151 | ``mark_changed()`` with the current session.
152 |
153 | .. code-block:: python
154 | :linenos:
155 | :emphasize-lines: 2,7
156 |
157 | from pyramid_sqlalchemy import Session
158 | from zope.sqlalchemy import mark_changed
159 | from myapp.models import MyModel
160 |
161 | session = Session()
162 | session.query(MyModel).update({'active': False})
163 | mark_changed(session)
164 |
--------------------------------------------------------------------------------
/docs/usage.rst:
--------------------------------------------------------------------------------
1 | Usage
2 | =====
3 |
4 | The ORM base class is available as :py:class:`pyramid_sqlalchemy.meta.BaseObject`
5 | and can be included directly:
6 |
7 | .. code-block:: python
8 |
9 | from pyramid_sqlalchemy import BaseObject
10 |
11 | class Account(BaseObject):
12 | __tablename__ = 'account'
13 | # Define your columns and methods here.
14 |
15 |
16 | When you need to build a query you can use the
17 | :py:obj:`pyramid_sqlalchemy.Session` session factory.
18 |
19 | .. code-block:: python
20 |
21 | from pyramid_sqlalchemy import Session
22 |
23 | account = Session.query(Account).first()
24 |
25 | When writing methods for a model in a you can also use
26 | :py:func:`sqlalchemy.orm.session.object_session` to get the current session for
27 | the object.
28 |
29 |
30 | .. code-block:: python
31 |
32 | from sqlalchemy.orm import object_session
33 |
34 | class Account(BaseObject):
35 | def favourites(self):
36 | """Return all the recent favourite articles."""
37 | session = object_session(self)
38 | return session.query(Article).all()
39 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bdist_wheel]
2 | universal=1
3 |
4 | [tool:pytest]
5 | testpaths = tests
6 | addopts = --tb=short
7 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from setuptools import setup
3 | from setuptools import find_packages
4 |
5 | version = '1.6'
6 |
7 | requires = [
8 | 'SQLAlchemy >=0.7.0',
9 | 'zope.sqlalchemy >=0.7.4',
10 | ]
11 |
12 | if sys.version_info[:2] < (3, 3):
13 | requires.append('mock')
14 |
15 | tests_require = ['pyramid']
16 |
17 | try:
18 | import unittest.mock
19 | except ImportError:
20 | tests_require.append('mock')
21 |
22 | setup(name='pyramid_sqlalchemy',
23 | version=version,
24 | description='SQLAlchemy integration for pyramid',
25 | long_description=open('README.rst').read() + '\n' +
26 | open('changes.rst').read(),
27 | classifiers=[
28 | 'Development Status :: 5 - Production/Stable',
29 | 'Intended Audience :: Developers',
30 | 'License :: OSI Approved :: BSD License',
31 | 'Framework :: Pyramid',
32 | 'Programming Language :: Python :: 2',
33 | 'Programming Language :: Python :: 2.6',
34 | 'Programming Language :: Python :: 2.7',
35 | 'Programming Language :: Python :: 3',
36 | 'Programming Language :: Python :: 3.4',
37 | 'Programming Language :: Python :: 3.5',
38 | 'Topic :: Database',
39 | 'Topic :: Software Development :: Libraries :: Python Modules',
40 | ],
41 | author='Wichert Akkerman',
42 | author_email='wichert@wiggy.net',
43 | url='https://pyramid-sqlalchemy.readthedocs.org',
44 | license='BSD',
45 | packages=find_packages('src'),
46 | package_dir={'': 'src'},
47 | include_package_data=True,
48 | zip_safe=True,
49 | install_requires=requires,
50 | tests_require=tests_require,
51 | extras_require={
52 | 'docs': ['sphinx'],
53 | 'tests': tests_require,
54 | },
55 | entry_points={
56 | 'pytest11': ['pyramid_sqlalchemy = pyramid_sqlalchemy.fixtures'],
57 | }
58 | )
59 |
--------------------------------------------------------------------------------
/src/pyramid_sqlalchemy/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from sqlalchemy import engine_from_config
3 | from pyramid_sqlalchemy.meta import metadata
4 | from pyramid_sqlalchemy.meta import BaseObject
5 | from pyramid_sqlalchemy.meta import Session
6 | import pyramid_sqlalchemy.events
7 | pyramid_sqlalchemy.events # Keep PyFlakes happy
8 |
9 |
10 | log = logging.getLogger('pyramid_sqlalchemy')
11 |
12 |
13 | def init_sqlalchemy(engine):
14 | """Initialise the SQLAlchemy models. This must be called before using
15 | using any of the SQLAlchemy managed the tables or classes in the model."""
16 | Session.configure(bind=engine)
17 | metadata.bind = engine
18 |
19 |
20 | def enable_sql_two_phase_commit(config, enable=True):
21 | Session.configure(twophase=enable)
22 |
23 |
24 | def includeme(config):
25 | """'Convenience method to initialise all components of this
26 | :mod:`pyramid_sqlalchemy` package from a pyramid applicaiton.
27 | """
28 | config.add_directive('enable_sql_two_phase_commit', enable_sql_two_phase_commit)
29 | engine = engine_from_config(config.registry.settings, 'sqlalchemy.')
30 | init_sqlalchemy(engine)
31 |
32 |
33 | __all__ = ['BaseObject', 'Session', 'metadata']
34 |
--------------------------------------------------------------------------------
/src/pyramid_sqlalchemy/events.py:
--------------------------------------------------------------------------------
1 | from sqlalchemy import event
2 | from pyramid_sqlalchemy import model
3 | from pyramid_sqlalchemy.meta import BaseObject
4 |
5 |
6 | @event.listens_for(BaseObject, 'class_instrument')
7 | def register_model(cls):
8 | setattr(model, cls.__name__, cls)
9 |
10 |
11 | @event.listens_for(BaseObject, 'class_uninstrument')
12 | def unregister_model(cls):
13 | if hasattr(model, cls.__name__):
14 | delattr(model, cls.__name__)
15 |
--------------------------------------------------------------------------------
/src/pyramid_sqlalchemy/fixtures.py:
--------------------------------------------------------------------------------
1 | try:
2 | from unittest import mock
3 | except ImportError:
4 | import mock
5 | import pytest
6 | from sqlalchemy import create_engine
7 | from .testing import DatabaseTestCase
8 | from . import Session
9 | from . import metadata
10 | from . import init_sqlalchemy
11 |
12 |
13 | DEFAULT_URI = 'sqlite:///'
14 |
15 |
16 | def pytest_addoption(parser):
17 | parser.addoption('--sql-url', default=DEFAULT_URI,
18 | help='SQLAlchemy Database URL')
19 | parser.addoption('--sql-echo', default=False, action='store_true',
20 | help='Echo SQL statements to console')
21 |
22 |
23 | def pytest_configure(config):
24 | DatabaseTestCase.db_uri = config.getoption('sql_url')
25 |
26 |
27 | @pytest.yield_fixture(scope='session')
28 | def _sqlalchemy(request):
29 |
30 | engine = create_engine(request.config.option.sql_url,
31 | echo=request.config.option.sql_echo)
32 |
33 | if engine.dialect.name == 'sqlite':
34 | engine.execute('PRAGMA foreign_keys = ON')
35 | # Check if a previous test has kept a session open. This will silently
36 | # make Session.configure do nothing and then break all our tests.
37 | assert not Session.registry.has()
38 | init_sqlalchemy(engine)
39 | metadata.create_all(engine)
40 |
41 | yield Session()
42 |
43 | Session.remove()
44 | metadata.drop_all(engine)
45 | Session.configure(bind=None)
46 | metadata.bind = None
47 | engine.dispose()
48 |
49 |
50 | @pytest.yield_fixture
51 | def transaction():
52 | """Create a new transaction for a test. The transaction is automatically
53 | marked as doomed to prevent it from being committed accidentily. At the end
54 | of the test it will be rolled back.
55 | """
56 | import transaction
57 | tx = transaction.begin()
58 | tx.doom() # Make sure a transaction can never be commited.
59 | # Mock out transaction.commit so code can never commit around us.
60 | with mock.patch('transaction.Transaction.commit'):
61 | yield
62 | tx.abort()
63 |
64 |
65 | @pytest.fixture
66 | def sql_session(transaction, _sqlalchemy, monkeypatch):
67 | """Provide a configured SQLAlchemy session running within a transaction.
68 | You can use the --sql-url commandline option to specify the SQL backend to
69 | use. The default configuration will use an in-memory SQLite database.
70 |
71 | You can also use the --sql-echo option to enable logging of all SQL
72 | statements to the console.
73 | """
74 | # SQL is already configured, so make sure it is not run again which would
75 | # result in a second connection.
76 | monkeypatch.setattr('pyramid_sqlalchemy.includeme', lambda c: None)
77 | return _sqlalchemy
78 |
79 |
80 | __all__ = ['transaction', 'sql_session']
81 |
--------------------------------------------------------------------------------
/src/pyramid_sqlalchemy/meta.py:
--------------------------------------------------------------------------------
1 | """SQLAlchemy Metadata and Session object"""
2 | from sqlalchemy import orm
3 | from sqlalchemy import schema
4 | from sqlalchemy.ext.declarative import declarative_base
5 | from zope.sqlalchemy import register
6 |
7 | #: SQLAlchemy session manager. Updated by
8 | # :py:func:`pyramid_sqlalchemy.init_sqlalchemy`.
9 | Session = orm.scoped_session(orm.sessionmaker())
10 | register(Session)
11 |
12 | #: Global metadata. If you have multiple databases with overlapping table
13 | #: names, you'll need a metadata for each database
14 | metadata = schema.MetaData()
15 |
16 | #: Base classes for models using declarative syntax
17 | BaseObject = declarative_base(metadata=metadata)
18 |
19 | __all__ = ['Session', 'metadata', 'BaseObject']
20 |
--------------------------------------------------------------------------------
/src/pyramid_sqlalchemy/model.py:
--------------------------------------------------------------------------------
1 | # This module will automatically be populated with all instrumented classes.
2 |
--------------------------------------------------------------------------------
/src/pyramid_sqlalchemy/testing.py:
--------------------------------------------------------------------------------
1 | try:
2 | from unittest import mock
3 | except ImportError:
4 | import mock
5 | import os
6 | import unittest
7 | from sqlalchemy import create_engine
8 | from pyramid_sqlalchemy import init_sqlalchemy
9 | from pyramid_sqlalchemy import metadata
10 | from pyramid_sqlalchemy import Session
11 | import transaction
12 |
13 |
14 | class DatabaseTestCase(unittest.TestCase):
15 | """Base class for tests which require a database connection.
16 |
17 | This class provides makes sure a SQL connection to a database is available
18 | for tests. Each test is run in a separate transaction, and all tables are
19 | recreated for each test to guarantee a clean slate.
20 | """
21 |
22 | #: FLag indicating if SQL tables should be created. This is normally
23 | #: set to `True`, but you may want to disabled table creation when writing
24 | #: tests for migration code.
25 | create_tables = True
26 |
27 | #: :ref:`Database URL ` for the test database.
28 | #: Normally a private in-memory SQLite database is used. You can override
29 | #: this with the pytest --sql-url parameter, or the DB_URI environment
30 | #: environment variable.
31 | db_uri = 'sqlite://'
32 |
33 | def database_url(self):
34 | return os.environ.get('DB_URI', self.db_uri)
35 |
36 | def setUp(self):
37 | self.engine = create_engine(self.database_url())
38 | if self.engine.dialect.name == 'sqlite':
39 | self.engine.execute('PRAGMA foreign_keys = ON')
40 | init_sqlalchemy(self.engine)
41 | if self.create_tables:
42 | metadata.create_all()
43 | super(DatabaseTestCase, self).setUp()
44 | self._sqlalchemy_patcher = mock.patch('pyramid_sqlalchemy.includeme', lambda c: None)
45 | self._sqlalchemy_patcher.start()
46 |
47 | def tearDown(self):
48 | transaction.abort()
49 | Session.remove()
50 | if self.create_tables:
51 | metadata.drop_all()
52 | Session.configure(bind=None)
53 | metadata.bind = None
54 | self.engine.dispose()
55 | self._sqlalchemy_patcher.stop()
56 | super(DatabaseTestCase, self).tearDown()
57 |
58 |
59 | __all__ = ['DatabaseTestCase']
60 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wichert/pyramid_sqlalchemy/b316350387ed8cedf49d14de048b716a6842c572/tests/__init__.py
--------------------------------------------------------------------------------
/tests/test_init.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from sqlalchemy import create_engine
3 | from pyramid.config import Configurator
4 | from pyramid_sqlalchemy import includeme
5 | from pyramid_sqlalchemy import init_sqlalchemy
6 | from pyramid_sqlalchemy import Session
7 |
8 |
9 | class test_init_sqlalchemy(unittest.TestCase):
10 | def test_basic_sqlite(self):
11 | engine = create_engine('sqlite://')
12 | init_sqlalchemy(engine)
13 | self.assertTrue(Session.session_factory.kw['bind'] is engine)
14 |
15 |
16 | class includeme_tests(unittest.TestCase):
17 | def test_sqlite_config(self):
18 | try:
19 | from unittest import mock
20 | except ImportError:
21 | import mock
22 |
23 | config = Configurator(settings={'sqlalchemy.url': 'sqlite://'})
24 |
25 | with mock.patch('pyramid_sqlalchemy.init_sqlalchemy') as init_sqlalchemy:
26 | includeme(config)
27 | self.assertTrue(init_sqlalchemy.called)
28 | engine = init_sqlalchemy.mock_calls[0][1][0]
29 | self.assertEqual(str(engine.url), 'sqlite://')
30 |
31 | def test_two_phase_directive(self):
32 | try:
33 | from unittest import mock
34 | except ImportError:
35 | import mock
36 |
37 | config = Configurator()
38 | with mock.patch('pyramid_sqlalchemy.Session.configure') as configure:
39 | with mock.patch('pyramid_sqlalchemy.engine_from_config'):
40 | with mock.patch('pyramid_sqlalchemy.init_sqlalchemy'):
41 | config.include('pyramid_sqlalchemy')
42 | config.enable_sql_two_phase_commit()
43 | configure.assert_called_with(twophase=True)
44 |
--------------------------------------------------------------------------------
/tests/test_testing.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import sqlalchemy.schema
3 | import sqlalchemy.types
4 | from pyramid_sqlalchemy.meta import BaseObject
5 | from pyramid_sqlalchemy.meta import Session
6 |
7 |
8 | class DatabaseTestCaseTests(unittest.TestCase):
9 | def DatabaseTestCase(self, *a, **kw):
10 | from pyramid_sqlalchemy.testing import DatabaseTestCase
11 |
12 | class MockCase(DatabaseTestCase):
13 | def runTest(self): # pragma: no cover
14 | pass
15 |
16 | return MockCase(*a, **kw)
17 |
18 | def test_tables_exist(self):
19 | from sqlalchemy.engine.reflection import Inspector
20 |
21 | testcase = self.DatabaseTestCase()
22 | try:
23 | testcase.setUp()
24 | inspector = Inspector.from_engine(Session.bind)
25 | self.assertTrue('dummy' in inspector.get_table_names())
26 | finally:
27 | testcase.tearDown()
28 |
29 | def test_no_leakage(self):
30 | testcase = self.DatabaseTestCase()
31 |
32 | class Dummy(BaseObject):
33 | __tablename__ = 'dummy'
34 |
35 | id = sqlalchemy.schema.Column(sqlalchemy.types.Integer(),
36 | primary_key=True, autoincrement=True)
37 |
38 | try:
39 | testcase.setUp()
40 | Session.add(Dummy())
41 | testcase.tearDown()
42 | testcase.setUp()
43 | self.assertEqual(Session.query(Dummy).count(), 0)
44 | finally:
45 | testcase.tearDown()
46 |
--------------------------------------------------------------------------------