Merge branch 'master' into feature/configurable-view-deriver
11 files added
75 files modified
| | |
| | | - python: 3.5 |
| | | env: TOXENV=py2-cover,py3-cover,coverage |
| | | - python: 3.5 |
| | | env: TOXENV=docs |
| | | - python: 3.5 |
| | | env: TOXENV=pep8 |
| | | |
| | | install: |
| | |
| | | notifications: |
| | | email: |
| | | - pyramid-checkins@lists.repoze.org |
| | | irc: |
| | | channels: |
| | | - "chat.freenode.net#pyramid" |
| | |
| | | 1.6 (2015-04-14) |
| | | ================ |
| | | |
| | | Backward Incompatibilities |
| | | -------------------------- |
| | | |
| | | - IPython and BPython support have been removed from pshell in the core. |
| | | To continue using them on Pyramid 1.6+ you must install the binding |
| | | packages explicitly:: |
| | | |
| | | $ pip install pyramid_ipython |
| | | |
| | | or |
| | | |
| | | $ pip install pyramid_bpython |
| | | |
| | | Features |
| | | -------- |
| | | |
| | |
| | | See https://github.com/Pylons/pyramid/pull/1471 |
| | | |
| | | - Cache busting for static resources has been added and is available via a new |
| | | argument to ``pyramid.config.Configurator.add_static_view``: ``cachebust``. |
| | | Core APIs are shipped for both cache busting via query strings and |
| | | path segments and may be extended to fit into custom asset pipelines. |
| | | ``pyramid.config.Configurator.add_cache_buster`` API. Core APIs are shipped |
| | | for both cache busting via query strings and via asset manifests for |
| | | integrating into custom asset pipelines. |
| | | See https://github.com/Pylons/pyramid/pull/1380 and |
| | | https://github.com/Pylons/pyramid/pull/1583 |
| | | https://github.com/Pylons/pyramid/pull/1583 and |
| | | https://github.com/Pylons/pyramid/pull/2171 |
| | | |
| | | - Add ``pyramid.config.Configurator.root_package`` attribute and init |
| | | parameter to assist with includeable packages that wish to resolve |
| | |
| | | https://github.com/Pylons/pyramid/pull/1610 |
| | | |
| | | - Additional shells for ``pshell`` can now be registered as entrypoints. See |
| | | https://github.com/Pylons/pyramid/pull/1891 |
| | | https://github.com/Pylons/pyramid/pull/1891 and |
| | | https://github.com/Pylons/pyramid/pull/2012 |
| | | |
| | | - The variables injected into ``pshell`` are now displayed with their |
| | | docstrings instead of the default ``str(obj)`` when possible. |
| | | See https://github.com/Pylons/pyramid/pull/1929 |
| | | |
| | | - ``pserve --reload`` will no longer crash on syntax errors!!! |
| | | See https://github.com/Pylons/pyramid/pull/2044 |
| | | |
| | | Bug Fixes |
| | | --------- |
| | |
| | | WebOb 1.5. |
| | | See https://github.com/Pylons/pyramid/pull/1865 |
| | | |
| | | - ``pshell`` will now preserve the capitalization of variables in the |
| | | ``[pshell]`` section of the INI file. This makes exposing classes to the |
| | | shell a little more straightfoward. |
| | | See https://github.com/Pylons/pyramid/pull/1883 |
| | | |
| | | - Fix an issue when user passes unparsed strings to ``pyramid.session.CookieSession`` |
| | | and ``pyramid.authentication.AuthTktCookieHelper`` for time related parameters |
| | | ``timeout``, ``reissue_time``, ``max_age`` that expect an integer value. |
| | | See https://github.com/Pylons/pyramid/pull/2050 |
| | | |
| | | - Fixed usage of ``pserve --monitor-restart --daemon`` which would fail in |
| | | horrible ways. See https://github.com/Pylons/pyramid/pull/2118 |
| | | |
| | | - Explicitly prevent ``pserve --reload --daemon`` from being used. It's never |
| | | been supported but would work and fail in weird ways. |
| | | See https://github.com/Pylons/pyramid/pull/2119 |
| | | |
| | | - Fix an issue on Windows when running ``pserve --reload`` in which the |
| | | process failed to fork because it could not find the pserve script to |
| | | run. See https://github.com/Pylons/pyramid/pull/2137 |
| | | |
| | | - Ensure that ``IAssetDescriptor.abspath`` always returns an absolute path. |
| | | There were cases depending on the process CWD that a relative path would |
| | | be returned. See https://github.com/Pylons/pyramid/issues/2187 |
| | | |
| | | |
| | | Deprecations |
| | | ------------ |
| | | |
| | | - The ``pserve`` command's daemonization features have been deprecated. This |
| | | includes the ``[start,stop,restart,status]`` subcommands as well as the |
| | | ``--daemon``, ``--stop-server``, ``--pid-file``, and ``--status`` flags. |
| | | - The ``pserve`` command's daemonization features have been deprecated as well |
| | | as ``--monitor-restart``. This includes the ``[start,stop,restart,status]`` |
| | | subcommands as well as the ``--daemon``, ``--stop-daemon``, ``--pid-file``, |
| | | ``--status``, ``--user`` and ``--group`` flags. |
| | | See https://github.com/Pylons/pyramid/pull/2120 |
| | | and https://github.com/Pylons/pyramid/pull/2189 |
| | | and https://github.com/Pylons/pyramid/pull/1641 |
| | | |
| | | Please use a real process manager in the future instead of relying on the |
| | | ``pserve`` to daemonize itself. Many options exist including your Operating |
| | |
| | | solutions like Circus and Supervisor. |
| | | |
| | | See https://github.com/Pylons/pyramid/pull/1641 |
| | | and https://github.com/Pylons/pyramid/pull/2120 |
| | | |
| | | - Renamed the ``principal`` argument to ``pyramid.security.remember()`` to |
| | | ``userid`` in order to clarify its intended purpose. |
| | |
| | | ``principal`` and a ``userid`` in its security APIs. |
| | | See https://github.com/Pylons/pyramid/pull/1399 |
| | | |
| | | - Add documentation of command line programs (``p*`` scripts). See |
| | | https://github.com/Pylons/pyramid/pull/2191 |
| | | |
| | | Scaffolds |
| | | --------- |
| | | |
| | |
| | | - Igor Stroh, 2015/06/10 |
| | | |
| | | - Jesse Dhillon, 2015/10/07 |
| | | |
| | | - Amos Latteier, 2015/10/22 |
| | | |
| | | - Rami Chousein, 2015/10/28 |
| | | |
| | | - Sri Sanketh Uppalapati, 2015/12/12 |
| | |
| | | - The feature must be documented in both the API and narrative |
| | | documentation (in ``docs/``). |
| | | |
| | | - The feature must work fully on the following CPython versions: 2.6, |
| | | 2.7, 3.2, and 3.3 on both UNIX and Windows. |
| | | - The feature must work fully on the following CPython versions: 2.6, 2.7, 3.2, |
| | | 3.3, 3.4, and 3.5 on both UNIX and Windows. |
| | | |
| | | - The feature must work on the latest version of PyPy. |
| | | - The feature must work on the latest version of PyPy and PyPy3. |
| | | |
| | | - The feature must not cause installation or runtime failure on App Engine. |
| | | If it doesn't cause installation or runtime failure, but doesn't actually |
| | |
| | | |
| | | Alternately:: |
| | | |
| | | $ tox -e{py26,py27,py32,py33,py34,pypy,pypy3}-scaffolds, |
| | | $ tox -e{py26,py27,py32,py33,py34,py35,pypy,pypy3}-scaffolds, |
| | | |
| | | Test Coverage |
| | | ------------- |
| | |
| | | or adds the feature. To build and review docs, use the following steps. |
| | | |
| | | 1. In the main Pyramid checkout directory, run ``./builddocs.sh`` (which just |
| | | turns around and runs ``tox -e py2-docs,py3-docs``):: |
| | | turns around and runs ``tox -e docs``):: |
| | | |
| | | $ ./builddocs.sh |
| | | |
| | |
| | | 1.5 (2014-04-08) |
| | | ================ |
| | | |
| | | - Python 3.4 compatibility. |
| | | |
| | | - Avoid crash in ``pserve --reload`` under Py3k, when iterating over possibly |
| | | mutated ``sys.modules``. |
| | | |
| | |
| | | Features |
| | | -------- |
| | | |
| | | - Python 3.3 compatibility. |
| | | |
| | | - Configurator.add_directive now accepts arbitrary callables like partials or |
| | | objects implementing ``__call__`` which dont have ``__name__`` and |
| | | ``__doc__`` attributes. See https://github.com/Pylons/pyramid/issues/621 |
| | |
| | | :target: https://webchat.freenode.net/?channels=pyramid |
| | | :alt: IRC Freenode |
| | | |
| | | Pyramid is a small, fast, down-to-earth, open source Python web framework. |
| | | It makes real-world web application development and |
| | | deployment more fun, more predictable, and more productive. |
| | | Pyramid is a small, fast, down-to-earth, open source Python web framework. It |
| | | makes real-world web application development and deployment more fun, more |
| | | predictable, and more productive. |
| | | |
| | | Pyramid is produced by the `Pylons Project <http://pylonsproject.org/>`_. |
| | | |
| | |
| | | See the `Pylons Project website <http://pylonsproject.org/>`_ to view |
| | | documentation, report bugs, and obtain support. |
| | | |
| | | Developing and Contributing |
| | | --------------------------- |
| | | |
| | | See ``HACKING.txt`` and ``contributing.md`` for guidelines for running tests, |
| | | adding features, coding style, and updating documentation when developing in or |
| | | contributing to Pyramid. |
| | | |
| | | License |
| | | ------- |
| | | |
| | |
| | | |
| | | - Run tests on Windows if feasible. |
| | | |
| | | - Make sure all scaffold tests pass (Py 2.6, 2.7, 3.2, 3.3, 3.4, pypy, and |
| | | - Make sure all scaffold tests pass (Py 2.6, 2.7, 3.2, 3.3, 3.4, 3.5, pypy, and |
| | | pypy3 on UNIX; this doesn't work on Windows): |
| | | |
| | | $ ./scaffoldtests.sh |
| | |
| | | Announcement template |
| | | ---------------------- |
| | | |
| | | Pyramid 1.1.X has been released. |
| | | Pyramid 1.X.X has been released. |
| | | |
| | | Here are the changes: |
| | | |
| | | <<changes>> |
| | | |
| | | A "What's New In Pyramid 1.1" document exists at |
| | | http://docs.pylonsproject.org/projects/pyramid/1.1/whatsnew-1.1.html . |
| | | A "What's New In Pyramid 1.X" document exists at |
| | | http://docs.pylonsproject.org/projects/pyramid/1.X/whatsnew-1.X.html . |
| | | |
| | | You will be able to see the 1.1 release documentation (across all |
| | | You will be able to see the 1.X release documentation (across all |
| | | alphas and betas, as well as when it eventually gets to final release) |
| | | at http://docs.pylonsproject.org/projects/pyramid/1.1/ . |
| | | at http://docs.pylonsproject.org/projects/pyramid/1.X/ . |
| | | |
| | | You can install it via PyPI: |
| | | |
| | | easy_install Pyramid==1.1a4 |
| | | easy_install Pyramid==1.X |
| | | |
| | | Enjoy, and please report any issues you find to the issue tracker at |
| | | https://github.com/Pylons/pyramid/issues |
| | |
| | | the templates chapter and elsewhere. Scan the documentation for reference |
| | | to a renderer as *only* view configuration (it's a larger concept now). |
| | | |
| | | - Add better docs about what-to-do-when-behind-a-proxy: paste.urlmap ("/foo = |
| | | - Add better docs about what-to-do-when-behind-a-proxy: rutter ("/foo = |
| | | app1" and "domain app1.localhost = app1"), ProxyPreserveHost and the nginx |
| | | equivalent, preserving HTTPS URLs. |
| | | |
| | | - Alias the stupid long default session factory name. |
| | | proxy_params, preserving HTTPS URLs. |
| | | |
| | | - Debug option to print view matching decision (e.g. debug_viewlookup or so). |
| | | |
| | |
| | | |
| | | - _fix_registry should dictify the registry being fixed. |
| | | |
| | | - Apply a prefix to the userid principal to avoid poisoning the principal |
| | | namespace. See https://github.com/Pylons/pyramid/issues/2060 |
| | |
| | | #!/bin/bash |
| | | tox -epy2-docs,py3-docs |
| | | tox -e docs |
| | | |
| | |
| | | |
| | | All projects under the Pylons Projects, including this one, follow the |
| | | guidelines established at [How to |
| | | Contribute](http://www.pylonsproject.org/community/how-to-contribute). |
| | | Contribute](http://www.pylonsproject.org/community/how-to-contribute) and |
| | | [Coding Style and |
| | | Standards](http://docs.pylonsproject.org/en/latest/community/codestyle.html). |
| | | |
| | | You can contribute to this project in several ways. |
| | | |
| | |
| | | Flow](https://guides.github.com/introduction/flow/index.html) describes the |
| | | workflow process and why it's a good practice. When submitting a pull |
| | | request, sign |
| | | [CONTRIBUTORS.txt](https://github.com/Pylons/pyramid/blob/master/CONTRIBUTORS. |
| | | txt) |
| | | [CONTRIBUTORS.txt](https://github.com/Pylons/pyramid/blob/master/CONTRIBUTORS.txt) |
| | | if you have not yet done so. |
| | | * Join the IRC channel #pyramid on irc.freenode.net. |
| | | |
| | | Git Branches |
| | | ------------ |
| | | Git branches and their purpose and status at the time of this writing are |
| | | listed below. |
| | | |
| | | * [master](https://github.com/Pylons/pyramid/) - The branch on which further |
| | | development takes place. The default branch on GitHub. |
| | | * [1.6-branch](https://github.com/Pylons/pyramid/tree/1.6-branch) - The branch |
| | | to which further development on master should be backported. This is also a |
| | | development branch. |
| | | * [1.5-branch](https://github.com/Pylons/pyramid/tree/1.5-branch) - The branch |
| | | classified as "stable" or "latest". Actively maintained. |
| | | * [1.4-branch](https://github.com/Pylons/pyramid/tree/1.4-branch) - The oldest |
| | | actively maintained and stable branch. |
| | | |
| | | Older branches are not actively maintained. In general, two stable branches and |
| | | one or two development branches are actively maintained. |
| | | |
| | | Prerequisites |
| | | ------------- |
| | |
| | | 1. Fork the repo on GitHub by clicking the [Fork] button. |
| | | 2. Clone your fork into a workspace on your local machine. |
| | | |
| | | git@github.com:<username>/pyramid.git |
| | | git clone git@github.com:<username>/pyramid.git |
| | | |
| | | 3. Add a git remote "upstream" for the cloned fork. |
| | | |
| | |
| | | load the built documentation in the `/_build/html/` directory in a web |
| | | browser. |
| | | |
| | | 6. From this point forward, follow the typical git workflow. Start by pulling |
| | | from the upstream to get the most current changes. |
| | | 6. From this point forward, follow the typical [git |
| | | workflow](https://help.github.com/articles/what-is-a-good-git-workflow/). |
| | | Start by pulling from the upstream to get the most current changes. |
| | | |
| | | git pull upstream master |
| | | |
| | |
| | | |
| | | .. autofunction:: BaseCookieSessionFactory |
| | | |
| | | .. autoclass:: PickleSerializer |
| | | |
| | |
| | | :members: |
| | | :inherited-members: |
| | | |
| | | .. autoclass:: PathSegmentCacheBuster |
| | | .. autoclass:: ManifestCacheBuster |
| | | :members: |
| | | |
| | | .. autoclass:: QueryStringCacheBuster |
| | | :members: |
| | | |
| | | .. autoclass:: PathSegmentMd5CacheBuster |
| | | :members: |
| | | |
| | | .. autoclass:: QueryStringMd5CacheBuster |
| | | :members: |
| | | |
| | | .. autoclass:: QueryStringConstantCacheBuster |
| | |
| | | 'sphinx.ext.doctest', |
| | | 'repoze.sphinx.autointerface', |
| | | 'sphinx.ext.viewcode', |
| | | 'sphinx.ext.intersphinx' |
| | | 'sphinx.ext.intersphinx', |
| | | 'sphinxcontrib.programoutput', |
| | | ] |
| | | |
| | | # Looks for objects in external projects |
| | | intersphinx_mapping = { |
| | | 'colander': ( 'http://docs.pylonsproject.org/projects/colander/en/latest', None), |
| | | 'colander': ('http://docs.pylonsproject.org/projects/colander/en/latest', None), |
| | | 'cookbook': ('http://docs.pylonsproject.org/projects/pyramid-cookbook/en/latest/', None), |
| | | 'deform': ('http://docs.pylonsproject.org/projects/deform/en/latest', None), |
| | | 'jinja2': ('http://docs.pylonsproject.org/projects/pyramid-jinja2/en/latest/', None), |
| | |
| | | 'python': ('http://docs.python.org', None), |
| | | 'python3': ('http://docs.python.org/3', None), |
| | | 'sqla': ('http://docs.sqlalchemy.org/en/latest', None), |
| | | 'tm': ('http://docs.pylonsproject.org/projects/pyramid_tm/en/latest/', None), |
| | | 'toolbar': ('http://docs.pylonsproject.org/projects/pyramid-debugtoolbar/en/latest', None), |
| | | 'tm': ('http://docs.pylonsproject.org/projects/pyramid_tm/en/latest/', None), |
| | | 'toolbar': ('http://docs.pylonsproject.org/projects/pyramid-debugtoolbar/en/latest', None), |
| | | 'tstring': ('http://docs.pylonsproject.org/projects/translationstring/en/latest', None), |
| | | 'tutorials': ('http://docs.pylonsproject.org/projects/pyramid-tutorials/en/latest/', None), |
| | | 'venusian': ('http://docs.pylonsproject.org/projects/venusian/en/latest', None), |
| | |
| | | lodged. To give context to discussions that follow, we detail some of the |
| | | design decisions and trade-offs here. In some cases, we acknowledge that the |
| | | framework can be made better and we describe future steps which will be taken |
| | | to improve it; in some cases we just file the challenge as noted, as |
| | | obviously you can't please everyone all of the time. |
| | | to improve it. In others we just file the challenge as noted, as obviously you |
| | | can't please everyone all of the time. |
| | | |
| | | Pyramid Provides More Than One Way to Do It |
| | | ------------------------------------------- |
| | | |
| | | A canon of Python popular culture is "TIOOWTDI" ("there is only one way to do |
| | | it", a slighting, tongue-in-cheek reference to Perl's "TIMTOWTDI", which is |
| | | an acronym for "there is more than one way to do it"). |
| | | it", a slighting, tongue-in-cheek reference to Perl's "TIMTOWTDI", which is an |
| | | acronym for "there is more than one way to do it"). |
| | | |
| | | :app:`Pyramid` is, for better or worse, a "TIMTOWTDI" system. For example, |
| | | it includes more than one way to resolve a URL to a :term:`view callable`: |
| | | via :term:`url dispatch` or :term:`traversal`. Multiple methods of |
| | | configuration exist: :term:`imperative configuration`, :term:`configuration |
| | | decoration`, and :term:`ZCML` (optionally via :term:`pyramid_zcml`). It works |
| | | with multiple different kinds of persistence and templating systems. And so |
| | | on. However, the existence of most of these overlapping ways to do things |
| | | are not without reason and purpose: we have a number of audiences to serve, |
| | | and we believe that TIMTOWTI at the web framework level actually *prevents* a |
| | | much more insidious and harmful set of duplication at higher levels in the |
| | | Python web community. |
| | | :app:`Pyramid` is, for better or worse, a "TIMTOWTDI" system. For example, it |
| | | includes more than one way to resolve a URL to a :term:`view callable`: via |
| | | :term:`url dispatch` or :term:`traversal`. Multiple methods of configuration |
| | | exist: :term:`imperative configuration`, :term:`configuration decoration`, and |
| | | :term:`ZCML` (optionally via :term:`pyramid_zcml`). It works with multiple |
| | | different kinds of persistence and templating systems. And so on. However, the |
| | | existence of most of these overlapping ways to do things are not without reason |
| | | and purpose: we have a number of audiences to serve, and we believe that |
| | | TIMTOWTDI at the web framework level actually *prevents* a much more insidious |
| | | and harmful set of duplication at higher levels in the Python web community. |
| | | |
| | | :app:`Pyramid` began its life as :mod:`repoze.bfg`, written by a team of |
| | | people with many years of prior :term:`Zope` experience. The idea of |
| | | :app:`Pyramid` began its life as :mod:`repoze.bfg`, written by a team of people |
| | | with many years of prior :term:`Zope` experience. The idea of |
| | | :term:`traversal` and the way :term:`view lookup` works was stolen entirely |
| | | from Zope. The authorization subsystem provided by :app:`Pyramid` is a |
| | | derivative of Zope's. The idea that an application can be *extended* without |
| | | forking is also a Zope derivative. |
| | | |
| | | Implementations of these features were *required* to allow the :app:`Pyramid` |
| | | authors to build the bread-and-butter CMS-type systems for customers in the |
| | | way in which they were accustomed. No other system, save for Zope itself, |
| | | had such features, and Zope itself was beginning to show signs of its age. |
| | | We were becoming hampered by consequences of its early design mistakes. |
| | | Zope's lack of documentation was also difficult to work around: it was hard |
| | | to hire smart people to work on Zope applications, because there was no |
| | | comprehensive documentation set to point them at which explained "it all" in |
| | | one consumable place, and it was too large and self-inconsistent to document |
| | | properly. Before :mod:`repoze.bfg` went under development, its authors |
| | | obviously looked around for other frameworks that fit the bill. But no |
| | | non-Zope framework did. So we embarked on building :mod:`repoze.bfg`. |
| | | authors to build the bread-and-butter CMS-type systems for customers in the way |
| | | in which they were accustomed. No other system, save for Zope itself, had such |
| | | features, and Zope itself was beginning to show signs of its age. We were |
| | | becoming hampered by consequences of its early design mistakes. Zope's lack of |
| | | documentation was also difficult to work around. It was hard to hire smart |
| | | people to work on Zope applications because there was no comprehensive |
| | | documentation set which explained "it all" in one consumable place, and it was |
| | | too large and self-inconsistent to document properly. Before :mod:`repoze.bfg` |
| | | went under development, its authors obviously looked around for other |
| | | frameworks that fit the bill. But no non-Zope framework did. So we embarked on |
| | | building :mod:`repoze.bfg`. |
| | | |
| | | As the result of our research, however, it became apparent that, despite the |
| | | fact that no *one* framework had all the features we required, lots of |
| | | existing frameworks had good, and sometimes very compelling ideas. In |
| | | particular, :term:`URL dispatch` is a more direct mechanism to map URLs to |
| | | code. |
| | | fact that no *one* framework had all the features we required, lots of existing |
| | | frameworks had good, and sometimes very compelling ideas. In particular, |
| | | :term:`URL dispatch` is a more direct mechanism to map URLs to code. |
| | | |
| | | So, although we couldn't find a framework, save for Zope, that fit our needs, |
| | | and while we incorporated a lot of Zope ideas into BFG, we also emulated the |
| | | features we found compelling in other frameworks (such as :term:`url |
| | | dispatch`). After the initial public release of BFG, as time went on, |
| | | features were added to support people allergic to various Zope-isms in the |
| | | system, such as the ability to configure the application using |
| | | :term:`imperative configuration` and :term:`configuration decoration` rather |
| | | than solely using :term:`ZCML`, and the elimination of the required use of |
| | | :term:`interface` objects. It soon became clear that we had a system that |
| | | was very generic, and was beginning to appeal to non-Zope users as well as |
| | | ex-Zope users. |
| | | dispatch`). After the initial public release of BFG, as time went on, features |
| | | were added to support people allergic to various Zope-isms in the system, such |
| | | as the ability to configure the application using :term:`imperative |
| | | configuration` and :term:`configuration decoration`, rather than solely using |
| | | :term:`ZCML`, and the elimination of the required use of :term:`interface` |
| | | objects. It soon became clear that we had a system that was very generic, and |
| | | was beginning to appeal to non-Zope users as well as ex-Zope users. |
| | | |
| | | As the result of this generalization, it became obvious BFG shared 90% of its |
| | | featureset with the featureset of Pylons 1, and thus had a very similar |
| | | target market. Because they were so similar, choosing between the two |
| | | systems was an exercise in frustration for an otherwise non-partisan |
| | | developer. It was also strange for the Pylons and BFG development |
| | | communities to be in competition for the same set of users, given how similar |
| | | the two frameworks were. So the Pylons and BFG teams began to work together |
| | | to form a plan to merge. The features missing from BFG (notably :term:`view |
| | | handler` classes, flash messaging, and other minor missing bits), were added, |
| | | to provide familiarity to ex-Pylons users. The result is :app:`Pyramid`. |
| | | feature set with the feature set of Pylons 1, and thus had a very similar |
| | | target market. Because they were so similar, choosing between the two systems |
| | | was an exercise in frustration for an otherwise non-partisan developer. It was |
| | | also strange for the Pylons and BFG development communities to be in |
| | | competition for the same set of users, given how similar the two frameworks |
| | | were. So the Pylons and BFG teams began to work together to form a plan to |
| | | merge. The features missing from BFG (notably :term:`view handler` classes, |
| | | flash messaging, and other minor missing bits), were added to provide |
| | | familiarity to ex-Pylons users. The result is :app:`Pyramid`. |
| | | |
| | | The Python web framework space is currently notoriously balkanized. We're |
| | | truly hoping that the amalgamation of components in :app:`Pyramid` will |
| | | appeal to at least two currently very distinct sets of users: Pylons and BFG |
| | | users. By unifying the best concepts from Pylons and BFG into a single |
| | | codebase and leaving the bad concepts from their ancestors behind, we'll be |
| | | able to consolidate our efforts better, share more code, and promote our |
| | | efforts as a unit rather than competing pointlessly. We hope to be able to |
| | | shortcut the pack mentality which results in a *much larger* duplication of |
| | | effort, represented by competing but incredibly similar applications and |
| | | libraries, each built upon a specific low level stack that is incompatible |
| | | with the other. We'll also shrink the choice of credible Python web |
| | | frameworks down by at least one. We're also hoping to attract users from |
| | | other communities (such as Zope's and TurboGears') by providing the features |
| | | they require, while allowing enough flexibility to do things in a familiar |
| | | fashion. Some overlap of functionality to achieve these goals is expected |
| | | and unavoidable, at least if we aim to prevent pointless duplication at |
| | | higher levels. If we've done our job well enough, the various audiences will |
| | | be able to coexist and cooperate rather than firing at each other across some |
| | | imaginary web framework DMZ. |
| | | The Python web framework space is currently notoriously balkanized. We're truly |
| | | hoping that the amalgamation of components in :app:`Pyramid` will appeal to at |
| | | least two currently very distinct sets of users: Pylons and BFG users. By |
| | | unifying the best concepts from Pylons and BFG into a single codebase, and |
| | | leaving the bad concepts from their ancestors behind, we'll be able to |
| | | consolidate our efforts better, share more code, and promote our efforts as a |
| | | unit rather than competing pointlessly. We hope to be able to shortcut the pack |
| | | mentality which results in a *much larger* duplication of effort, represented |
| | | by competing but incredibly similar applications and libraries, each built upon |
| | | a specific low level stack that is incompatible with the other. We'll also |
| | | shrink the choice of credible Python web frameworks down by at least one. We're |
| | | also hoping to attract users from other communities (such as Zope's and |
| | | TurboGears') by providing the features they require, while allowing enough |
| | | flexibility to do things in a familiar fashion. Some overlap of functionality |
| | | to achieve these goals is expected and unavoidable, at least if we aim to |
| | | prevent pointless duplication at higher levels. If we've done our job well |
| | | enough, the various audiences will be able to coexist and cooperate rather than |
| | | firing at each other across some imaginary web framework DMZ. |
| | | |
| | | Pyramid Uses A Zope Component Architecture ("ZCA") Registry |
| | | Pyramid Uses a Zope Component Architecture ("ZCA") Registry |
| | | ----------------------------------------------------------- |
| | | |
| | | :app:`Pyramid` uses a :term:`Zope Component Architecture` (ZCA) "component |
| | |
| | | problem number two. |
| | | |
| | | Third of all, what does the ``getUtility`` function do? It's performing a |
| | | lookup for the ``ISettings`` "utility" that should return.. well, a utility. |
| | | lookup for the ``ISettings`` "utility" that should return... well, a utility. |
| | | Note how we've already built up a dependency on the understanding of an |
| | | :term:`interface` and the concept of "utility" to answer this question: a bad |
| | | sign so far. Note also that the answer is circular, a *really* bad sign. |
| | |
| | | |
| | | Fifth, assuming you buy that there's some magical registry hanging around, |
| | | where *is* this registry? *Homina homina*... "around"? That's sort of the |
| | | best answer in this context (a more specific answer would require knowledge |
| | | of internals). Can there be more than one registry? Yes. So *which* |
| | | registry does it find the registration in? Well, the "current" registry of |
| | | course. In terms of :app:`Pyramid`, the current registry is a thread local |
| | | variable. Using an API that consults a thread local makes understanding how |
| | | it works non-local. |
| | | best answer in this context (a more specific answer would require knowledge of |
| | | internals). Can there be more than one registry? Yes. So in *which* registry |
| | | does it find the registration? Well, the "current" registry of course. In |
| | | terms of :app:`Pyramid`, the current registry is a thread local variable. |
| | | Using an API that consults a thread local makes understanding how it works |
| | | non-local. |
| | | |
| | | You've now bought in to the fact that there's a registry that is just hanging |
| | | around. But how does the registry get populated? Why, via code that calls |
| | |
| | | it's not present in any user configuration. This is extremely hard to |
| | | comprehend. Problem number six. |
| | | |
| | | Clearly there's some amount of cognitive load here that needs to be borne by |
| | | a reader of code that extends the :app:`Pyramid` framework due to its use of |
| | | the ZCA, even if he or she is already an expert Python programmer and whom is |
| | | an expert in the domain of web applications. This is suboptimal. |
| | | Clearly there's some amount of cognitive load here that needs to be borne by a |
| | | reader of code that extends the :app:`Pyramid` framework due to its use of the |
| | | ZCA, even if they are already an expert Python programmer and an expert in the |
| | | domain of web applications. This is suboptimal. |
| | | |
| | | Ameliorations |
| | | +++++++++++++ |
| | |
| | | |
| | | .. _http_exception_hierarchy: |
| | | |
| | | Pyramid Uses its Own HTTP Exception Class Hierarchy Rather Than ``webob.exc`` |
| | | ----------------------------------------------------------------------------- |
| | | Pyramid uses its own HTTP exception class hierarchy rather than :mod:`webob.exc` |
| | | -------------------------------------------------------------------------------- |
| | | |
| | | .. versionadded:: 1.1 |
| | | |
| | | The HTTP exception classes defined in :mod:`pyramid.httpexceptions` are very |
| | | much like the ones defined in ``webob.exc`` |
| | | (e.g. :class:`~pyramid.httpexceptions.HTTPNotFound`, |
| | | :class:`~pyramid.httpexceptions.HTTPForbidden`, etc). They have the same |
| | | names and largely the same behavior and all have a very similar |
| | | implementation, but not the same identity. Here's why they have a separate |
| | | identity: |
| | | much like the ones defined in :mod:`webob.exc`, (e.g., |
| | | :class:`~pyramid.httpexceptions.HTTPNotFound` or |
| | | :class:`~pyramid.httpexceptions.HTTPForbidden`). They have the same names and |
| | | largely the same behavior, and all have a very similar implementation, but not |
| | | the same identity. Here's why they have a separate identity: |
| | | |
| | | - Making them separate allows the HTTP exception classes to subclass |
| | | :class:`pyramid.response.Response`. This speeds up response generation |
| | | slightly due to the way the Pyramid router works. The same speedup could |
| | | be gained by monkeypatching ``webob.response.Response`` but it's usually |
| | | slightly due to the way the Pyramid router works. The same speedup could be |
| | | gained by monkeypatching :class:`webob.response.Response`, but it's usually |
| | | the case that monkeypatching turns out to be evil and wrong. |
| | | |
| | | - Making them separate allows them to provide alternate ``__call__`` logic |
| | |
| | | value of ``RequestClass`` (:class:`pyramid.request.Request`). |
| | | |
| | | - Making them separate allows us freedom from having to think about backwards |
| | | compatibility code present in ``webob.exc`` having to do with Python 2.4, |
| | | compatibility code present in :mod:`webob.exc` having to do with Python 2.4, |
| | | which we no longer support in Pyramid 1.1+. |
| | | |
| | | - We change the behavior of two classes |
| | |
| | | - Making them separate allows us to influence the docstrings of the exception |
| | | classes to provide Pyramid-specific documentation. |
| | | |
| | | - Making them separate allows us to silence a stupid deprecation warning |
| | | under Python 2.6 when the response objects are used as exceptions (related |
| | | to ``self.message``). |
| | | - Making them separate allows us to silence a stupid deprecation warning under |
| | | Python 2.6 when the response objects are used as exceptions (related to |
| | | ``self.message``). |
| | | |
| | | .. _simpler_traversal_model: |
| | | |
| | |
| | | (Allow, 'bob', 'read'), (Deny, 'fred', 'write')]``. If an ACL is |
| | | attached to a resource instance, and that resource is findable via the |
| | | context resource, it will be consulted any active security policy to |
| | | determine wither a particular request can be fulfilled given the |
| | | determine whether a particular request can be fulfilled given the |
| | | :term:`authentication` information in the request. |
| | | |
| | | authentication |
| | |
| | | data in a Redis database. See |
| | | https://pypi.python.org/pypi/pyramid_redis_sessions for more information. |
| | | |
| | | cache busting |
| | | A technique used when serving a cacheable static asset in order to force |
| | | a client to query the new version of the asset. See :ref:`cache_busting` |
| | | for more information. |
| | |
| | | The Pyramid Web Framework |
| | | ========================= |
| | | |
| | | :app:`Pyramid` is a small, fast, down-to-earth Python web framework. It |
| | | is developed as part of the `Pylons Project |
| | | <http://docs.pylonsproject.org/>`_. It is licensed under a `BSD-like license |
| | | <http://repoze.org/license.html>`_. |
| | | :app:`Pyramid` is a small, fast, down-to-earth Python web framework. It is |
| | | developed as part of the `Pylons Project <http://docs.pylonsproject.org/>`_. |
| | | It is licensed under a `BSD-like license <http://repoze.org/license.html>`_. |
| | | |
| | | Here is one of the simplest :app:`Pyramid` applications you can make: |
| | | |
| | |
| | | |
| | | After you install :app:`Pyramid` and run this application, when you visit |
| | | `<http://localhost:8080/hello/world>`_ in a browser, you will see the text |
| | | ``Hello, world!`` |
| | | ``Hello, world!`` See :ref:`firstapp_chapter` for a full explanation of how |
| | | this application works. |
| | | |
| | | See :ref:`firstapp_chapter` for a full explanation of how this application |
| | | works. Read the :ref:`html_narrative_documentation` to understand how |
| | | :app:`Pyramid` is designed to scale from simple applications like this to |
| | | very large web applications. To just dive in headfirst, read the |
| | | :doc:`quick_tour`. |
| | | |
| | | Front Matter |
| | | ============ |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | |
| | | copyright.rst |
| | | conventions.rst |
| | | |
| | | .. _html_getting_started: |
| | | |
| | | Getting Started |
| | | =============== |
| | | |
| | | If you are new to Pyramid, we have a few resources that can help you get |
| | | up to speed right away. |
| | | If you are new to Pyramid, we have a few resources that can help you get up to |
| | | speed right away. |
| | | |
| | | .. toctree:: |
| | | :hidden: |
| | |
| | | quick_tour |
| | | quick_tutorial/index |
| | | |
| | | * :doc:`quick_tour` goes through the major features in Pyramid, covering |
| | | a little about a lot. |
| | | * :doc:`quick_tour` gives an overview of the major features in Pyramid, |
| | | covering a little about a lot. |
| | | |
| | | * :doc:`quick_tutorial/index` does the same, but in a tutorial format: |
| | | deeper treatment of each topic and with working code. |
| | | * :doc:`quick_tutorial/index` is similar to the Quick Tour, but in a tutorial |
| | | format, with somewhat deeper treatment of each topic and with working code. |
| | | |
| | | * To see a minimal Pyramid web application, check out |
| | | :ref:`firstapp_chapter`. |
| | | * Like learning by example? Visit the official :ref:`html_tutorials` as well as |
| | | the community-contributed :ref:`Pyramid tutorials |
| | | <tutorials:pyramid-tutorials>`, which include a :ref:`Todo List Application |
| | | in One File <tutorials:single-file-tutorial>`. |
| | | |
| | | * For help getting Pyramid set up, try |
| | | :ref:`installing_chapter`. |
| | | * For help getting Pyramid set up, try :ref:`installing_chapter`. |
| | | |
| | | * Like learning by example? Visit the official |
| | | :doc:`wiki tutorial <../tutorials/wiki2/index>` as well as the |
| | | community-contributed |
| | | :ref:`Pyramid tutorials <tutorials:pyramid-tutorials>`, which include |
| | | a :ref:`single file tasks tutorial <tutorials:single-file-tutorial>`. |
| | | * Need help? See :ref:`Support and Development <support-and-development>`. |
| | | |
| | | * Need help? See :ref:`Support and |
| | | Development <support-and-development>`. |
| | | |
| | | .. _html_tutorials: |
| | | |
| | | Tutorials |
| | | ========= |
| | | |
| | | Official tutorials explaining how to use :app:`Pyramid` to build various types |
| | | of applications, and how to deploy :app:`Pyramid` applications to various |
| | | platforms. |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | |
| | | tutorials/wiki2/index.rst |
| | | tutorials/wiki/index.rst |
| | | tutorials/modwsgi/index.rst |
| | | |
| | | |
| | | .. _support-and-development: |
| | | |
| | | Support and Development |
| | | ======================= |
| | | |
| | | The `Pylons Project web site <http://pylonsproject.org/>`_ is the main online |
| | | source of :app:`Pyramid` support and development information. |
| | | |
| | | To report bugs, use the `issue tracker |
| | | <https://github.com/Pylons/pyramid/issues>`_. |
| | | |
| | | If you've got questions that aren't answered by this documentation, contact the |
| | | `Pylons-discuss maillist <http://groups.google.com/group/pylons-discuss>`_ or |
| | | join the `#pyramid IRC channel <irc://irc.freenode.net/#pyramid>`_. |
| | | |
| | | Browse and check out tagged and trunk versions of :app:`Pyramid` via the |
| | | `Pyramid GitHub repository <https://github.com/Pylons/pyramid/>`_. To check out |
| | | the trunk via ``git``, use either command: |
| | | |
| | | .. code-block:: text |
| | | |
| | | # If you have SSH keys configured on GitHub: |
| | | git clone git@github.com:Pylons/pyramid.git |
| | | |
| | | # Otherwise, HTTPS will work, using your GitHub login: |
| | | git clone https://github.com/Pylons/pyramid.git |
| | | |
| | | To find out how to become a contributor to :app:`Pyramid`, please see the |
| | | `contributor's section of the documentation |
| | | <http://docs.pylonsproject.org/en/latest/#contributing>`_. |
| | | |
| | | |
| | | .. _html_narrative_documentation: |
| | |
| | | Narrative Documentation |
| | | ======================= |
| | | |
| | | Narrative documentation in chapter form explaining how to use |
| | | :app:`Pyramid`. |
| | | Narrative documentation in chapter form explaining how to use :app:`Pyramid`. |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 2 |
| | |
| | | narr/threadlocals |
| | | narr/zca |
| | | |
| | | .. _html_tutorials: |
| | | |
| | | Tutorials |
| | | ========= |
| | | |
| | | Tutorials explaining how to use :app:`Pyramid` to build various types of |
| | | applications, and how to deploy :app:`Pyramid` applications to various |
| | | platforms. |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 2 |
| | | |
| | | tutorials/wiki2/index.rst |
| | | tutorials/wiki/index.rst |
| | | tutorials/modwsgi/index.rst |
| | | |
| | | API Documentation |
| | | ================= |
| | | |
| | | Comprehensive reference material for every public API exposed by :app:`Pyramid`: |
| | | Comprehensive reference material for every public API exposed by |
| | | :app:`Pyramid`: |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | |
| | | |
| | | api/index |
| | | api/* |
| | | |
| | | |
| | | ``p*`` Scripts Documentation |
| | | ============================ |
| | | |
| | | ``p*`` scripts included with :app:`Pyramid`:. |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | :glob: |
| | | |
| | | pscripts/index |
| | | pscripts/* |
| | | |
| | | |
| | | Change History |
| | | ============== |
| | |
| | | whatsnew-1.0 |
| | | changes |
| | | |
| | | |
| | | Design Documents |
| | | ================ |
| | | |
| | |
| | | |
| | | designdefense |
| | | |
| | | .. _support-and-development: |
| | | |
| | | Support and Development |
| | | ======================= |
| | | Copyright, Trademarks, and Attributions |
| | | ======================================= |
| | | |
| | | The `Pylons Project web site <http://pylonsproject.org/>`_ is the main online |
| | | source of :app:`Pyramid` support and development information. |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | |
| | | To report bugs, use the `issue tracker |
| | | <https://github.com/Pylons/pyramid/issues>`_. |
| | | copyright |
| | | |
| | | If you've got questions that aren't answered by this documentation, |
| | | contact the `Pylons-discuss maillist |
| | | <http://groups.google.com/group/pylons-discuss>`_ or join the `#pyramid |
| | | IRC channel <irc://irc.freenode.net/#pyramid>`_. |
| | | |
| | | Browse and check out tagged and trunk versions of :app:`Pyramid` via |
| | | the `Pyramid GitHub repository <https://github.com/Pylons/pyramid/>`_. |
| | | To check out the trunk via ``git``, use this command: |
| | | Typographical Conventions |
| | | ========================= |
| | | |
| | | .. code-block:: text |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | |
| | | git clone git@github.com:Pylons/pyramid.git |
| | | conventions |
| | | |
| | | To find out how to become a contributor to :app:`Pyramid`, please see the |
| | | `contributor's section of the documentation |
| | | <http://docs.pylonsproject.org/en/latest/#contributing>`_. |
| | | |
| | | Index and Glossary |
| | | ================== |
| | |
| | | pyramid.debug_notfound = false |
| | | pyramid.debug_routematch = false |
| | | pyramid.default_locale_name = en |
| | | pyramid.includes = |
| | | pyramid.includes = |
| | | pyramid_debugtoolbar |
| | | |
| | | # By default, the toolbar only appears for clients from IP addresses |
| | |
| | | |
| | | [server:main] |
| | | use = egg:waitress#main |
| | | host = 0.0.0.0 |
| | | host = 127.0.0.1 |
| | | port = 6543 |
| | | |
| | | ### |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | |
| | | <meta name="author" content="Pylons Project"> |
| | | <link rel="shortcut icon" href="${request.static_url('myproject:static/pyramid-16x16.png')}"> |
| | | |
| | | <title>Starter Template for The Pyramid Web Framework</title> |
| | | <title>Starter Scaffold for The Pyramid Web Framework</title> |
| | | |
| | | <!-- Bootstrap core CSS --> |
| | | <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet"> |
| | | |
| | | <!-- Custom styles for this template --> |
| | | <!-- Custom styles for this scaffold --> |
| | | <link href="${request.static_url('myproject:static/theme.css')}" rel="stylesheet"> |
| | | |
| | | <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --> |
| | |
| | | </div> |
| | | <div class="col-md-10"> |
| | | <div class="content"> |
| | | <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">starter template</span></h1> |
| | | <p class="lead">Welcome to <span class="font-normal">${project}</span>, an application generated by<br>the <span class="font-normal">Pyramid Web Framework</span>.</p> |
| | | <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Starter scaffold</span></h1> |
| | | <p class="lead">Welcome to <span class="font-normal">${project}</span>, an application generated by<br>the <span class="font-normal">Pyramid Web Framework 1.6b2</span>.</p> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="row"> |
| | | <div class="links"> |
| | | <ul> |
| | | <li class="current-version">Currently v1.5</li> |
| | | <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org">Docs</a></li> |
| | | <li class="current-version">Generated by v1.6b2</li> |
| | | <li><i class="glyphicon glyphicon-bookmark icon-muted"></i><a href="http://docs.pylonsproject.org/projects/pyramid/en/1.6-branch/">Docs</a></li> |
| | | <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li> |
| | | <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="irc://irc.freenode.net#pyramid">IRC Channel</a></li> |
| | | <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="http://pylonsproject.org">Pylons Project</a></li> |
| | | </ul> |
| | | </div> |
| | | </div> |
| | | <div class="row"> |
| | |
| | | info = my_view(request) |
| | | self.assertEqual(info['project'], 'MyProject') |
| | | |
| | | class ViewIntegrationTests(unittest.TestCase): |
| | | def setUp(self): |
| | | """ This sets up the application registry with the |
| | | registrations your application declares in its ``includeme`` |
| | | function. |
| | | """ |
| | | self.config = testing.setUp() |
| | | self.config.include('myproject') |
| | | |
| | | def tearDown(self): |
| | | """ Clear out the application registry """ |
| | | testing.tearDown() |
| | | |
| | | def test_my_view(self): |
| | | from myproject.views import my_view |
| | | request = testing.DummyRequest() |
| | | result = my_view(request) |
| | | self.assertEqual(result.status, '200 OK') |
| | | body = result.app_iter[0] |
| | | self.assertTrue('Welcome to' in body) |
| | | self.assertEqual(len(result.headerlist), 2) |
| | | self.assertEqual(result.headerlist[0], |
| | | ('Content-Type', 'text/html; charset=UTF-8')) |
| | | self.assertEqual(result.headerlist[1], ('Content-Length', |
| | | str(len(body)))) |
| | | |
| | | class FunctionalTests(unittest.TestCase): |
| | | def setUp(self): |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | |
| | | """Setup for the MyProject package. |
| | | |
| | | """ |
| | | import os |
| | | |
| | | from setuptools import setup, find_packages |
| | | |
| | | here = os.path.abspath(os.path.dirname(__file__)) |
| | | with open(os.path.join(here, 'README.txt')) as f: |
| | | README = f.read() |
| | | with open(os.path.join(here, 'CHANGES.txt')) as f: |
| | | CHANGES = f.read() |
| | | |
| | | HERE = os.path.abspath(os.path.dirname(__file__)) |
| | | |
| | | |
| | | with open(os.path.join(HERE, 'README.txt')) as fp: |
| | | README = fp.read() |
| | | |
| | | |
| | | with open(os.path.join(HERE, 'CHANGES.txt')) as fp: |
| | | CHANGES = fp.read() |
| | | |
| | | |
| | | REQUIRES = [ |
| | | requires = [ |
| | | 'pyramid', |
| | | 'pyramid_chameleon', |
| | | 'pyramid_debugtoolbar', |
| | | 'waitress', |
| | | ] |
| | | |
| | | TESTS_REQUIRE = [ |
| | | 'webtest' |
| | | ] |
| | | |
| | | setup(name='MyProject', |
| | |
| | | description='MyProject', |
| | | long_description=README + '\n\n' + CHANGES, |
| | | classifiers=[ |
| | | 'Programming Language :: Python', |
| | | 'Framework :: Pyramid', |
| | | 'Topic :: Internet :: WWW/HTTP', |
| | | 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', |
| | | ], |
| | | "Programming Language :: Python", |
| | | "Framework :: Pyramid", |
| | | "Topic :: Internet :: WWW/HTTP", |
| | | "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", |
| | | ], |
| | | author='', |
| | | author_email='', |
| | | url='', |
| | |
| | | packages=find_packages(), |
| | | include_package_data=True, |
| | | zip_safe=False, |
| | | install_requires=REQUIRES, |
| | | tests_require=TESTS_REQUIRE, |
| | | test_suite='myproject', |
| | | install_requires=requires, |
| | | tests_require=requires, |
| | | test_suite="myproject", |
| | | entry_points="""\ |
| | | [paste.app_factory] |
| | | main = myproject:main |
| | | """) |
| | | """, |
| | | ) |
| | |
| | | Advanced Configuration |
| | | ====================== |
| | | |
| | | To support application extensibility, the :app:`Pyramid` |
| | | :term:`Configurator`, by default, detects configuration conflicts and allows |
| | | you to include configuration imperatively from other packages or modules. It |
| | | also, by default, performs configuration in two separate phases. This allows |
| | | you to ignore relative configuration statement ordering in some |
| | | circumstances. |
| | | To support application extensibility, the :app:`Pyramid` :term:`Configurator` |
| | | by default detects configuration conflicts and allows you to include |
| | | configuration imperatively from other packages or modules. It also by default |
| | | performs configuration in two separate phases. This allows you to ignore |
| | | relative configuration statement ordering in some circumstances. |
| | | |
| | | .. index:: |
| | | pair: configuration; conflict detection |
| | |
| | | server = make_server('0.0.0.0', 8080, app) |
| | | server.serve_forever() |
| | | |
| | | The application now has two conflicting view configuration statements. When |
| | | we try to start it again, it won't start. Instead, we'll receive a traceback |
| | | that ends something like this: |
| | | The application now has two conflicting view configuration statements. When we |
| | | try to start it again, it won't start. Instead we'll receive a traceback that |
| | | ends something like this: |
| | | |
| | | .. code-block:: guess |
| | | .. code-block:: text |
| | | :linenos: |
| | | |
| | | Traceback (most recent call last): |
| | |
| | | |
| | | This traceback is trying to tell us: |
| | | |
| | | - We've got conflicting information for a set of view configuration |
| | | statements (The ``For:`` line). |
| | | - We've got conflicting information for a set of view configuration statements |
| | | (The ``For:`` line). |
| | | |
| | | - There are two statements which conflict, shown beneath the ``For:`` line: |
| | | ``config.add_view(hello_world. 'hello')`` on line 14 of ``app.py``, and |
| | | ``config.add_view(goodbye_world, 'hello')`` on line 17 of ``app.py``. |
| | | |
| | | These two configuration statements are in conflict because we've tried to |
| | | tell the system that the set of :term:`predicate` values for both view |
| | | These two configuration statements are in conflict because we've tried to tell |
| | | the system that the set of :term:`predicate` values for both view |
| | | configurations are exactly the same. Both the ``hello_world`` and |
| | | ``goodbye_world`` views are configured to respond under the same set of |
| | | circumstances. This circumstance: the :term:`view name` (represented by the |
| | | ``name=`` predicate) is ``hello``. |
| | | circumstances. This circumstance, the :term:`view name` represented by the |
| | | ``name=`` predicate, is ``hello``. |
| | | |
| | | This presents an ambiguity that :app:`Pyramid` cannot resolve. Rather than |
| | | allowing the circumstance to go unreported, by default Pyramid raises a |
| | |
| | | modify your configuration code accordingly. |
| | | |
| | | If you're getting a conflict while trying to extend an existing application, |
| | | and that application has a function which performs configuration like this |
| | | one: |
| | | and that application has a function which performs configuration like this one: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | def add_routes(config): |
| | | config.add_route(...) |
| | | |
| | | Don't call this function directly with ``config`` as an argument. Instead, |
| | | use :meth:`pyramid.config.Configurator.include`: |
| | | Don't call this function directly with ``config`` as an argument. Instead, use |
| | | :meth:`pyramid.config.Configurator.include`: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | config.include(add_routes) |
| | | |
| | | Using :meth:`~pyramid.config.Configurator.include` instead of calling the |
| | | function directly provides a modicum of automated conflict resolution, with |
| | | the configuration statements you define in the calling code overriding those |
| | | of the included function. |
| | | function directly provides a modicum of automated conflict resolution, with the |
| | | configuration statements you define in the calling code overriding those of the |
| | | included function. |
| | | |
| | | .. seealso:: |
| | | |
| | |
| | | +++++++++++++++++++++++++ |
| | | |
| | | You can manually commit a configuration by using the |
| | | :meth:`~pyramid.config.Configurator.commit` method between configuration |
| | | calls. For example, we prevent conflicts from occurring in the application |
| | | we examined previously as the result of adding a ``commit``. Here's the |
| | | application that generates conflicts: |
| | | :meth:`~pyramid.config.Configurator.commit` method between configuration calls. |
| | | For example, we prevent conflicts from occurring in the application we examined |
| | | previously as the result of adding a ``commit``. Here's the application that |
| | | generates conflicts: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | server = make_server('0.0.0.0', 8080, app) |
| | | server.serve_forever() |
| | | |
| | | We can prevent the two ``add_view`` calls from conflicting by issuing a call |
| | | to :meth:`~pyramid.config.Configurator.commit` between them: |
| | | We can prevent the two ``add_view`` calls from conflicting by issuing a call to |
| | | :meth:`~pyramid.config.Configurator.commit` between them: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | :emphasize-lines: 16 |
| | | |
| | | from wsgiref.simple_server import make_server |
| | | from pyramid.config import Configurator |
| | |
| | | server.serve_forever() |
| | | |
| | | In the above example we've issued a call to |
| | | :meth:`~pyramid.config.Configurator.commit` between the two ``add_view`` |
| | | calls. :meth:`~pyramid.config.Configurator.commit` will execute any pending |
| | | :meth:`~pyramid.config.Configurator.commit` between the two ``add_view`` calls. |
| | | :meth:`~pyramid.config.Configurator.commit` will execute any pending |
| | | configuration statements. |
| | | |
| | | Calling :meth:`~pyramid.config.Configurator.commit` is safe at any time. It |
| | | executes all pending configuration actions and leaves the configuration |
| | | action list "clean". |
| | | executes all pending configuration actions and leaves the configuration action |
| | | list "clean". |
| | | |
| | | Note that :meth:`~pyramid.config.Configurator.commit` has no effect when |
| | | you're using an *autocommitting* configurator (see |
| | | :ref:`autocommitting_configurator`). |
| | | Note that :meth:`~pyramid.config.Configurator.commit` has no effect when you're |
| | | using an *autocommitting* configurator (see :ref:`autocommitting_configurator`). |
| | | |
| | | .. _autocommitting_configurator: |
| | | |
| | | Using An Autocommitting Configurator |
| | | Using an Autocommitting Configurator |
| | | ++++++++++++++++++++++++++++++++++++ |
| | | |
| | | You can also use a heavy hammer to circumvent conflict detection by using a |
| | |
| | | If your code uses the :meth:`~pyramid.config.Configurator.include` method to |
| | | include external configuration, some conflicts are automatically resolved. |
| | | Configuration statements that are made as the result of an "include" will be |
| | | overridden by configuration statements that happen within the caller of |
| | | the "include" method. |
| | | overridden by configuration statements that happen within the caller of the |
| | | "include" method. |
| | | |
| | | Automatic conflict resolution supports this goal: if a user wants to reuse a |
| | | Automatic conflict resolution supports this goal. If a user wants to reuse a |
| | | Pyramid application, and they want to customize the configuration of this |
| | | application without hacking its code "from outside", they can "include" a |
| | | configuration function from the package and override only some of its |
| | | configuration statements within the code that does the include. No conflicts |
| | | will be generated by configuration statements within the code that does the |
| | | including, even if configuration statements in the included code would |
| | | conflict if it was moved "up" to the calling code. |
| | | including, even if configuration statements in the included code would conflict |
| | | if it was moved "up" to the calling code. |
| | | |
| | | Methods Which Provide Conflict Detection |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | |
| | | :meth:`~pyramid.config.Configurator.add_resource_url_adapter`, |
| | | and :meth:`~pyramid.config.Configurator.add_response_adapter`. |
| | | |
| | | :meth:`~pyramid.config.Configurator.add_static_view` also indirectly |
| | | provides conflict detection, because it's implemented in terms of the |
| | | conflict-aware ``add_route`` and ``add_view`` methods. |
| | | :meth:`~pyramid.config.Configurator.add_static_view` also indirectly provides |
| | | conflict detection, because it's implemented in terms of the conflict-aware |
| | | ``add_route`` and ``add_view`` methods. |
| | | |
| | | .. index:: |
| | | pair: configuration; including from external sources |
| | |
| | | Including Configuration from External Sources |
| | | --------------------------------------------- |
| | | |
| | | Some application programmers will factor their configuration code in such a |
| | | way that it is easy to reuse and override configuration statements. For |
| | | example, such a developer might factor out a function used to add routes to |
| | | his application: |
| | | Some application programmers will factor their configuration code in such a way |
| | | that it is easy to reuse and override configuration statements. For example, |
| | | such a developer might factor out a function used to add routes to their |
| | | application: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | def add_routes(config): |
| | | config.add_route(...) |
| | | |
| | | Rather than calling this function directly with ``config`` as an argument. |
| | | Instead, use :meth:`pyramid.config.Configurator.include`: |
| | | Rather than calling this function directly with ``config`` as an argument, |
| | | instead use :meth:`pyramid.config.Configurator.include`: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | :meth:`~pyramid.config.Configurator.include` can also accept a :term:`dotted |
| | | Python name` to a function or a module. |
| | | |
| | | .. note: See :ref:`the_include_tag` for a declarative alternative to |
| | | the :meth:`~pyramid.config.Configurator.include` method. |
| | | .. note:: See :ref:`the_include_tag` for a declarative alternative to the |
| | | :meth:`~pyramid.config.Configurator.include` method. |
| | | |
| | | .. _twophase_config: |
| | | |
| | | Two-Phase Configuration |
| | | ----------------------- |
| | | |
| | | When a non-autocommitting :term:`Configurator` is used to do configuration |
| | | (the default), configuration execution happens in two phases. In the first |
| | | phase, "eager" configuration actions (actions that must happen before all |
| | | others, such as registering a renderer) are executed, and *discriminators* |
| | | are computed for each of the actions that depend on the result of the eager |
| | | actions. In the second phase, the discriminators of all actions are compared |
| | | to do conflict detection. |
| | | When a non-autocommitting :term:`Configurator` is used to do configuration (the |
| | | default), configuration execution happens in two phases. In the first phase, |
| | | "eager" configuration actions (actions that must happen before all others, such |
| | | as registering a renderer) are executed, and *discriminators* are computed for |
| | | each of the actions that depend on the result of the eager actions. In the |
| | | second phase, the discriminators of all actions are compared to do conflict |
| | | detection. |
| | | |
| | | Due to this, for configuration methods that have no internal ordering |
| | | constraints, execution order of configuration method calls is not important. |
| | |
| | | config.add_view('some.view', renderer='path_to_custom/renderer.rn') |
| | | |
| | | Even though the view statement depends on the registration of a custom |
| | | renderer, due to two-phase configuration, the order in which the |
| | | configuration statements are issued is not important. ``add_view`` will be |
| | | able to find the ``.rn`` renderer even if ``add_renderer`` is called after |
| | | ``add_view``. |
| | | renderer, due to two-phase configuration, the order in which the configuration |
| | | statements are issued is not important. ``add_view`` will be able to find the |
| | | ``.rn`` renderer even if ``add_renderer`` is called after ``add_view``. |
| | | |
| | | The same is untrue when you use an *autocommitting* configurator (see |
| | | :ref:`autocommitting_configurator`). When an autocommitting configurator is |
| | | used, two-phase configuration is disabled, and configuration statements must |
| | | be ordered in dependency order. |
| | | used, two-phase configuration is disabled, and configuration statements must be |
| | | ordered in dependency order. |
| | | |
| | | Some configuration methods, such as |
| | | :meth:`~pyramid.config.Configurator.add_route` have internal ordering |
| | |
| | | More Information |
| | | ---------------- |
| | | |
| | | For more information, see the article, `"A Whirlwind Tour of Advanced |
| | | For more information, see the article `"A Whirlwind Tour of Advanced |
| | | Configuration Tactics" |
| | | <http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/configuration/whirlwind_tour.html>`_, |
| | | <http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/configuration/whirlwind_tour.html>`_ |
| | | in the Pyramid Cookbook. |
| | |
| | | Under normal circumstances you'd just need to wait for the client's cached copy |
| | | to expire before they get the new version of the static resource. |
| | | |
| | | A commonly used workaround to this problem is a technique known as "cache |
| | | busting". Cache busting schemes generally involve generating a URL for a |
| | | static asset that changes when the static asset changes. This way headers can |
| | | be sent along with the static asset instructing the client to cache the asset |
| | | for a very long time. When a static asset is changed, the URL used to refer to |
| | | it in a web page also changes, so the client sees it as a new resource and |
| | | requests the asset, regardless of any caching policy set for the resource's old |
| | | URL. |
| | | A commonly used workaround to this problem is a technique known as |
| | | :term:`cache busting`. Cache busting schemes generally involve generating a |
| | | URL for a static asset that changes when the static asset changes. This way |
| | | headers can be sent along with the static asset instructing the client to cache |
| | | the asset for a very long time. When a static asset is changed, the URL used |
| | | to refer to it in a web page also changes, so the client sees it as a new |
| | | resource and requests the asset, regardless of any caching policy set for the |
| | | resource's old URL. |
| | | |
| | | :app:`Pyramid` can be configured to produce cache busting URLs for static |
| | | assets by passing the optional argument, ``cachebust`` to |
| | | :meth:`~pyramid.config.Configurator.add_static_view`: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | # config is an instance of pyramid.config.Configurator |
| | | config.add_static_view(name='static', path='mypackage:folder/static', |
| | | cachebust=True) |
| | | |
| | | Setting the ``cachebust`` argument instructs :app:`Pyramid` to use a cache |
| | | busting scheme which adds the md5 checksum for a static asset as a path segment |
| | | in the asset's URL: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | js_url = request.static_url('mypackage:folder/static/js/myapp.js') |
| | | # Returns: 'http://www.example.com/static/c9658b3c0a314a1ca21e5988e662a09e/js/myapp.js' |
| | | |
| | | When the asset changes, so will its md5 checksum, and therefore so will its |
| | | URL. Supplying the ``cachebust`` argument also causes the static view to set |
| | | headers instructing clients to cache the asset for ten years, unless the |
| | | ``cache_max_age`` argument is also passed, in which case that value is used. |
| | | |
| | | .. note:: |
| | | |
| | | md5 checksums are cached in RAM, so if you change a static resource without |
| | | restarting your application, you may still generate URLs with a stale md5 |
| | | checksum. |
| | | |
| | | Disabling the Cache Buster |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | It can be useful in some situations (e.g., development) to globally disable all |
| | | configured cache busters without changing calls to |
| | | :meth:`~pyramid.config.Configurator.add_static_view`. To do this set the |
| | | ``PYRAMID_PREVENT_CACHEBUST`` environment variable or the |
| | | ``pyramid.prevent_cachebust`` configuration value to a true value. |
| | | |
| | | Customizing the Cache Buster |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Revisiting from the previous section: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | # config is an instance of pyramid.config.Configurator |
| | | config.add_static_view(name='static', path='mypackage:folder/static', |
| | | cachebust=True) |
| | | |
| | | Setting ``cachebust`` to ``True`` instructs :app:`Pyramid` to use a default |
| | | cache busting implementation that should work for many situations. The |
| | | ``cachebust`` may be set to any object that implements the interface |
| | | :class:`~pyramid.interfaces.ICacheBuster`. The above configuration is exactly |
| | | equivalent to: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.static import PathSegmentMd5CacheBuster |
| | | |
| | | # config is an instance of pyramid.config.Configurator |
| | | config.add_static_view(name='static', path='mypackage:folder/static', |
| | | cachebust=PathSegmentMd5CacheBuster()) |
| | | |
| | | :app:`Pyramid` includes a handful of ready to use cache buster implementations: |
| | | :class:`~pyramid.static.PathSegmentMd5CacheBuster`, which inserts an md5 |
| | | checksum token in the path portion of the asset's URL, |
| | | :class:`~pyramid.static.QueryStringMd5CacheBuster`, which adds an md5 checksum |
| | | token to the query string of the asset's URL, and |
| | | :class:`~pyramid.static.QueryStringConstantCacheBuster`, which adds an |
| | | arbitrary token you provide to the query string of the asset's URL. |
| | | |
| | | In order to implement your own cache buster, you can write your own class from |
| | | scratch which implements the :class:`~pyramid.interfaces.ICacheBuster` |
| | | interface. Alternatively you may choose to subclass one of the existing |
| | | implementations. One of the most likely scenarios is you'd want to change the |
| | | way the asset token is generated. To do this just subclass either |
| | | :class:`~pyramid.static.PathSegmentCacheBuster` or |
| | | :class:`~pyramid.static.QueryStringCacheBuster` and define a |
| | | ``tokenize(pathspec)`` method. Here is an example which just uses Git to get |
| | | the hash of the currently checked out code: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | import os |
| | | import subprocess |
| | | from pyramid.static import PathSegmentCacheBuster |
| | | |
| | | class GitCacheBuster(PathSegmentCacheBuster): |
| | | """ |
| | | Assuming your code is installed as a Git checkout, as opposed to an egg |
| | | from an egg repository like PYPI, you can use this cachebuster to get |
| | | the current commit's SHA1 to use as the cache bust token. |
| | | """ |
| | | def __init__(self): |
| | | here = os.path.dirname(os.path.abspath(__file__)) |
| | | self.sha1 = subprocess.check_output( |
| | | ['git', 'rev-parse', 'HEAD'], |
| | | cwd=here).strip() |
| | | |
| | | def tokenize(self, pathspec): |
| | | return self.sha1 |
| | | |
| | | Choosing a Cache Buster |
| | | ~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | The default cache buster implementation, |
| | | :class:`~pyramid.static.PathSegmentMd5CacheBuster`, works very well assuming |
| | | that you're using :app:`Pyramid` to serve your static assets. The md5 checksum |
| | | is fine grained enough that browsers should only request new versions of |
| | | specific assets that have changed. Many caching HTTP proxies will fail to |
| | | cache a resource if the URL contains a query string. In general, therefore, |
| | | you should prefer a cache busting strategy which modifies the path segment to a |
| | | strategy which adds a query string. |
| | | |
| | | It is possible, however, that your static assets are being served by another |
| | | web server or externally on a CDN. In these cases modifying the path segment |
| | | for a static asset URL would cause the external service to fail to find the |
| | | asset, causing your customer to get a 404. In these cases you would need to |
| | | fall back to a cache buster which adds a query string. It is even possible |
| | | that there isn't a copy of your static assets available to the :app:`Pyramid` |
| | | application, so a cache busting implementation that generates md5 checksums |
| | | would fail since it can't access the assets. In such a case, |
| | | :class:`~pyramid.static.QueryStringConstantCacheBuster` is a reasonable |
| | | fallback. The following code would set up a cachebuster that just uses the |
| | | time at start up as a cachebust token: |
| | | assets using :meth:`~pyramid.config.Configurator.add_cache_buster`: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | import time |
| | | from pyramid.static import QueryStringConstantCacheBuster |
| | | |
| | | # config is an instance of pyramid.config.Configurator |
| | | config.add_static_view(name='static', path='mypackage:folder/static/') |
| | | config.add_cache_buster( |
| | | 'mypackage:folder/static/', |
| | | QueryStringConstantCacheBuster(str(int(time.time())))) |
| | | |
| | | Adding the cachebuster instructs :app:`Pyramid` to add the current time for |
| | | a static asset to the query string in the asset's URL: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | js_url = request.static_url('mypackage:folder/static/js/myapp.js') |
| | | # Returns: 'http://www.example.com/static/js/myapp.js?x=1445318121' |
| | | |
| | | When the web server restarts, the time constant will change and therefore so |
| | | will its URL. |
| | | |
| | | .. note:: |
| | | |
| | | Cache busting is an inherently complex topic as it integrates the asset |
| | | pipeline and the web application. It is expected and desired that |
| | | application authors will write their own cache buster implementations |
| | | conforming to the properties of their own asset pipelines. See |
| | | :ref:`custom_cache_busters` for information on writing your own. |
| | | |
| | | Disabling the Cache Buster |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | It can be useful in some situations (e.g., development) to globally disable all |
| | | configured cache busters without changing calls to |
| | | :meth:`~pyramid.config.Configurator.add_cache_buster`. To do this set the |
| | | ``PYRAMID_PREVENT_CACHEBUST`` environment variable or the |
| | | ``pyramid.prevent_cachebust`` configuration value to a true value. |
| | | |
| | | .. _custom_cache_busters: |
| | | |
| | | Customizing the Cache Buster |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Calls to :meth:`~pyramid.config.Configurator.add_cache_buster` may use |
| | | any object that implements the interface |
| | | :class:`~pyramid.interfaces.ICacheBuster`. |
| | | |
| | | :app:`Pyramid` ships with a very simplistic |
| | | :class:`~pyramid.static.QueryStringConstantCacheBuster`, which adds an |
| | | arbitrary token you provide to the query string of the asset's URL. This |
| | | is almost never what you want in production as it does not allow fine-grained |
| | | busting of individual assets. |
| | | |
| | | In order to implement your own cache buster, you can write your own class from |
| | | scratch which implements the :class:`~pyramid.interfaces.ICacheBuster` |
| | | interface. Alternatively you may choose to subclass one of the existing |
| | | implementations. One of the most likely scenarios is you'd want to change the |
| | | way the asset token is generated. To do this just subclass |
| | | :class:`~pyramid.static.QueryStringCacheBuster` and define a |
| | | ``tokenize(pathspec)`` method. Here is an example which uses Git to get |
| | | the hash of the current commit: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | import os |
| | | import subprocess |
| | | from pyramid.static import QueryStringCacheBuster |
| | | |
| | | class GitCacheBuster(QueryStringCacheBuster): |
| | | """ |
| | | Assuming your code is installed as a Git checkout, as opposed to an egg |
| | | from an egg repository like PYPI, you can use this cachebuster to get |
| | | the current commit's SHA1 to use as the cache bust token. |
| | | """ |
| | | def __init__(self, param='x', repo_path=None): |
| | | super(GitCacheBuster, self).__init__(param=param) |
| | | if repo_path is None: |
| | | repo_path = os.path.dirname(os.path.abspath(__file__)) |
| | | self.sha1 = subprocess.check_output( |
| | | ['git', 'rev-parse', 'HEAD'], |
| | | cwd=repo_path).strip() |
| | | |
| | | def tokenize(self, pathspec): |
| | | return self.sha1 |
| | | |
| | | A simple cache buster that modifies the path segment can be constructed as |
| | | well: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | import posixpath |
| | | |
| | | class PathConstantCacheBuster(object): |
| | | def __init__(self, token): |
| | | self.token = token |
| | | |
| | | def __call__(self, request, subpath, kw): |
| | | base_subpath, ext = posixpath.splitext(subpath) |
| | | new_subpath = base_subpath + self.token + ext |
| | | return new_subpath, kw |
| | | |
| | | The caveat with this approach is that modifying the path segment |
| | | changes the file name, and thus must match what is actually on the |
| | | filesystem in order for :meth:`~pyramid.config.Configurator.add_static_view` |
| | | to find the file. It's better to use the |
| | | :class:`~pyramid.static.ManifestCacheBuster` for these situations, as |
| | | described in the next section. |
| | | |
| | | .. _path_segment_cache_busters: |
| | | |
| | | Path Segments and Choosing a Cache Buster |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Many caching HTTP proxies will fail to cache a resource if the URL contains |
| | | a query string. Therefore, in general, you should prefer a cache busting |
| | | strategy which modifies the path segment rather than methods which add a |
| | | token to the query string. |
| | | |
| | | You will need to consider whether the :app:`Pyramid` application will be |
| | | serving your static assets, whether you are using an external asset pipeline |
| | | to handle rewriting urls internal to the css/javascript, and how fine-grained |
| | | do you want the cache busting tokens to be. |
| | | |
| | | In many cases you will want to host the static assets on another web server |
| | | or externally on a CDN. In these cases your :app:`Pyramid` application may not |
| | | even have access to a copy of the static assets. In order to cache bust these |
| | | assets you will need some information about them. |
| | | |
| | | If you are using an external asset pipeline to generate your static files you |
| | | should consider using the :class:`~pyramid.static.ManifestCacheBuster`. |
| | | This cache buster can load a standard JSON formatted file generated by your |
| | | pipeline and use it to cache bust the assets. This has many performance |
| | | advantages as :app:`Pyramid` does not need to look at the files to generate |
| | | any cache busting tokens, but still supports fine-grained per-file tokens. |
| | | |
| | | Assuming an example ``manifest.json`` like: |
| | | |
| | | .. code-block:: json |
| | | |
| | | { |
| | | "css/main.css": "css/main-678b7c80.css", |
| | | "images/background.png": "images/background-a8169106.png" |
| | | } |
| | | |
| | | The following code would set up a cachebuster: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.static import ManifestCacheBuster |
| | | |
| | | config.add_static_view( |
| | | name='http://mycdn.example.com/', |
| | | path='mypackage:static', |
| | | cachebust=QueryStringConstantCacheBuster(str(time.time()))) |
| | | path='mypackage:static') |
| | | |
| | | config.add_cache_buster( |
| | | 'mypackage:static/', |
| | | ManifestCacheBuster('myapp:static/manifest.json')) |
| | | |
| | | It's important to note that the cache buster only handles generating |
| | | cache-busted URLs for static assets. It does **NOT** provide any solutions for |
| | | serving those assets. For example, if you generated a URL for |
| | | ``css/main-678b7c80.css`` then that URL needs to be valid either by |
| | | configuring ``add_static_view`` properly to point to the location of the files |
| | | or some other mechanism such as the files existing on your CDN or rewriting |
| | | the incoming URL to remove the cache bust tokens. |
| | | |
| | | .. index:: |
| | | single: static assets view |
| | |
| | | Often one needs to refer to images and other static assets inside CSS and |
| | | JavaScript files. If cache busting is active, the final static asset URL is not |
| | | available until the static assets have been assembled. These URLs cannot be |
| | | handwritten. Thus, when having static asset references in CSS and JavaScript, |
| | | one needs to perform one of the following tasks. |
| | | handwritten. Below is an example of how to integrate the cache buster into |
| | | the entire stack. Remember, it is just an example and should be modified to |
| | | fit your specific tools. |
| | | |
| | | * Process the files by using a precompiler which rewrites URLs to their final |
| | | cache busted form. |
| | | * First, process the files by using a precompiler which rewrites URLs to their |
| | | final cache-busted form. Then, you can use the |
| | | :class:`~pyramid.static.ManifestCacheBuster` to synchronize your asset |
| | | pipeline with :app:`Pyramid`, allowing the pipeline to have full control |
| | | over the final URLs of your assets. |
| | | |
| | | Now that you are able to generate static URLs within :app:`Pyramid`, |
| | | you'll need to handle URLs that are out of our control. To do this you may |
| | | use some of the following options to get started: |
| | | |
| | | * Configure your asset pipeline to rewrite URL references inline in |
| | | CSS and JavaScript. This is the best approach because then the files |
| | | may be hosted by :app:`Pyramid` or an external CDN without having to |
| | | change anything. They really are static. |
| | | |
| | | * Templatize JS and CSS, and call ``request.static_url()`` inside their |
| | | template code. |
| | | template code. While this approach may work in certain scenarios, it is not |
| | | recommended because your static assets will not really be static and are now |
| | | dependent on :app:`Pyramid` to be served correctly. See |
| | | :ref:`advanced_static` for more information on this approach. |
| | | |
| | | * Pass static URL references to CSS and JavaScript via other means. |
| | | |
| | | Below are some simple approaches for CSS and JS programming which consider |
| | | asset cache busting. These approaches do not require additional tools or |
| | | packages. |
| | | |
| | | Relative cache busted URLs in CSS |
| | | +++++++++++++++++++++++++++++++++ |
| | | |
| | | Consider a CSS file ``/static/theme/css/site.css`` which contains the following |
| | | CSS code. |
| | | |
| | | .. code-block:: css |
| | | |
| | | body { |
| | | background: url(/static/theme/img/background.jpg); |
| | | } |
| | | |
| | | Any changes to ``background.jpg`` would not appear to the visitor because the |
| | | URL path is not cache busted as it is. Instead we would have to construct an |
| | | URL to the background image with the default ``PathSegmentCacheBuster`` cache |
| | | busting mechanism:: |
| | | |
| | | https://site/static/1eeb262c717/theme/img/background.jpg |
| | | |
| | | Every time the image is updated, the URL would need to be changed. It is not |
| | | practical to write this non-human readable URL into a CSS file. |
| | | |
| | | However, the CSS file itself is cache busted and is located under the path for |
| | | static assets. This lets us use relative references in our CSS to cache bust |
| | | the image. |
| | | |
| | | .. code-block:: css |
| | | |
| | | body { |
| | | background: url(../img/background.jpg); |
| | | } |
| | | |
| | | The browser would interpret this as having the CSS file hash in URL:: |
| | | |
| | | https://site/static/ab234b262c71/theme/css/../img/background.jpg |
| | | |
| | | The downside of this approach is that if the background image changes, one |
| | | needs to bump the CSS file. The CSS file hash change signals the caches that |
| | | the relative URL to the image in the CSS has been changed. When updating CSS |
| | | and related image assets, updates usually happen hand in hand, so this does not |
| | | add extra effort to theming workflow. |
| | | |
| | | Passing cache busted URLs to JavaScript |
| | | +++++++++++++++++++++++++++++++++++++++ |
| | | |
| | | For JavaScript, one can pass static asset URLs as function arguments or |
| | | globals. The globals can be generated in page template code, having access to |
| | | the ``request.static_url()`` function. |
| | | |
| | | Below is a simple example of passing a cached busted image URL in the Jinja2 |
| | | template language. Put the following code into the ``<head>`` section of the |
| | | relevant page. |
| | | |
| | | .. code-block:: html |
| | | |
| | | <script> |
| | | window.assets.backgroundImage = |
| | | "{{ '/theme/img/background.jpg'|static_url() }}"; |
| | | </script> |
| | | |
| | | Then in your main ``site.js`` file, put the following code. |
| | | |
| | | .. code-block:: javascript |
| | | |
| | | var image = new Image(window.assets.backgroundImage); |
| | | If your CSS and JavaScript assets use URLs to reference other assets it is |
| | | recommended that you implement an external asset pipeline that can rewrite the |
| | | generated static files with new URLs containing cache busting tokens. The |
| | | machinery inside :app:`Pyramid` will not help with this step as it has very |
| | | little knowledge of the asset types your application may use. The integration |
| | | into :app:`Pyramid` is simply for linking those assets into your HTML and |
| | | other dynamic content. |
| | | |
| | | .. _advanced_static: |
| | | |
| | |
| | | As of Pyramid 1.6, it is also possible to override an asset by supplying an |
| | | absolute path to a file or directory. This may be useful if the assets are |
| | | not distributed as part of a Python package. |
| | | |
| | | Cache Busting and Asset Overrides |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Overriding static assets that are being hosted using |
| | | :meth:`pyramid.config.Configurator.add_static_view` can affect your cache |
| | | busting strategy when using any cache busters that are asset-aware such as |
| | | :class:`pyramid.static.ManifestCacheBuster`. What sets asset-aware cache |
| | | busters apart is that they have logic tied to specific assets. For example, |
| | | a manifest is only generated for a specific set of pre-defined assets. Now, |
| | | imagine you have overridden an asset defined in this manifest with a new, |
| | | unknown version. By default, the cache buster will be invoked for an asset |
| | | it has never seen before and will likely end up returning a cache busting |
| | | token for the original asset rather than the asset that will actually end up |
| | | being served! In order to get around this issue, it's possible to attach a |
| | | different :class:`pyramid.interfaces.ICacheBuster` implementation to the |
| | | new assets. This would cause the original assets to be served by their |
| | | manifest, and the new assets served by their own cache buster. To do this, |
| | | :meth:`pyramid.config.Configurator.add_cache_buster` supports an ``explicit`` |
| | | option. For example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.static import ManifestCacheBuster |
| | | |
| | | # define a static view for myapp:static assets |
| | | config.add_static_view('static', 'myapp:static') |
| | | |
| | | # setup a cache buster for your app based on the myapp:static assets |
| | | my_cb = ManifestCacheBuster('myapp:static/manifest.json') |
| | | config.add_cache_buster('myapp:static', my_cb) |
| | | |
| | | # override an asset |
| | | config.override_asset( |
| | | to_override='myapp:static/background.png', |
| | | override_with='theme:static/background.png') |
| | | |
| | | # override the cache buster for theme:static assets |
| | | theme_cb = ManifestCacheBuster('theme:static/manifest.json') |
| | | config.add_cache_buster('theme:static', theme_cb, explicit=True) |
| | | |
| | | In the above example there is a default cache buster, ``my_cb``, for all |
| | | assets served from the ``myapp:static`` folder. This would also affect |
| | | ``theme:static/background.png`` when generating URLs via |
| | | ``request.static_url('myapp:static/background.png')``. |
| | | |
| | | The ``theme_cb`` is defined explicitly for any assets loaded from the |
| | | ``theme:static`` folder. Explicit cache busters have priority and thus |
| | | ``theme_cb`` would be invoked for |
| | | ``request.static_url('myapp:static/background.png')``, but ``my_cb`` would |
| | | be used for any other assets like |
| | | ``request.static_url('myapp:static/favicon.ico')``. |
| | |
| | | Your :app:`Pyramid` application can be controlled and inspected using a variety |
| | | of command-line utilities. These utilities are documented in this chapter. |
| | | |
| | | |
| | | .. index:: |
| | | pair: matching views; printing |
| | | single: pviews |
| | |
| | | |
| | | Displaying Matching Views for a Given URL |
| | | ----------------------------------------- |
| | | |
| | | .. seealso:: See also the output of :ref:`pviews --help <pviews_script>`. |
| | | |
| | | For a big application with several views, it can be hard to keep the view |
| | | configuration details in your head, even if you defined all the views yourself. |
| | |
| | | |
| | | .. index:: |
| | | single: interactive shell |
| | | single: IPython |
| | | single: pshell |
| | | single: bpython |
| | | |
| | | .. _interactive_shell: |
| | | |
| | | The Interactive Shell |
| | | --------------------- |
| | | |
| | | .. seealso:: See also the output of :ref:`pshell --help <pshell_script>`. |
| | | |
| | | Once you've installed your program for development using ``setup.py develop``, |
| | | you can use an interactive Python shell to execute expressions in a Python |
| | |
| | | $ $VENV/bin/pshell starter/development.ini |
| | | |
| | | Press ``Ctrl-D`` to exit the interactive shell (or ``Ctrl-Z`` on Windows). |
| | | |
| | | |
| | | .. index:: |
| | | pair: pshell; extending |
| | |
| | | >>> request.route_url('home') |
| | | 'https://www.example.com/' |
| | | |
| | | .. index:: |
| | | single: IPython |
| | | single: bpython |
| | | |
| | | .. _ipython_or_bpython: |
| | | |
| | | IPython or bpython |
| | | Alternative Shells |
| | | ~~~~~~~~~~~~~~~~~~ |
| | | |
| | | If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ and/or `bpython |
| | | <http://bpython-interpreter.org/>`_ in the interpreter you use to invoke the |
| | | ``pshell`` command, ``pshell`` will autodiscover and use the first one found, |
| | | in this order: IPython, bpython, standard Python interpreter. However you could |
| | | specifically invoke your choice with the ``-p choice`` or ``--python-shell |
| | | choice`` option. |
| | | The ``pshell`` command can be easily extended with alternate REPLs if the |
| | | default python REPL is not satisfactory. Assuming you have a binding |
| | | installed such as ``pyramid_ipython`` it will normally be auto-selected and |
| | | used. You may also specifically invoke your choice with the ``-p choice`` or |
| | | ``--python-shell choice`` option. |
| | | |
| | | .. code-block:: text |
| | | |
| | | $ $VENV/bin/pshell -p ipython | bpython | python development.ini#MyProject |
| | | $ $VENV/bin/pshell -p ipython development.ini#MyProject |
| | | |
| | | Alternative Shells |
| | | ~~~~~~~~~~~~~~~~~~ |
| | | You may use the ``--list-shells`` option to see the available shells. |
| | | |
| | | .. code-block:: text |
| | | |
| | | $ $VENV/bin/pshell --list-shells |
| | | Available shells: |
| | | bpython |
| | | ipython |
| | | python |
| | | |
| | | If you want to use a shell that isn't supported out of the box, you can |
| | | introduce a new shell by registering an entry point in your setup.py: |
| | | |
| | | .. code-block:: python |
| | | |
| | | setup( |
| | | entry_points = """\ |
| | | [pyramid.pshell] |
| | | myshell=my_app:ptpython_shell_factory |
| | | """ |
| | | entry_points={ |
| | | 'pyramid.pshell_runner': [ |
| | | 'myshell=my_app:ptpython_shell_factory', |
| | | ], |
| | | }, |
| | | ) |
| | | |
| | | And then your shell factory should return a function that accepts two |
| | |
| | | |
| | | .. code-block:: python |
| | | |
| | | def ptpython_shell_factory(): |
| | | from ptpython.repl import embed |
| | | def PTPShell(banner, **kwargs): |
| | | print(banner) |
| | | return embed(**kwargs) |
| | | from ptpython.repl import embed |
| | | |
| | | def shell(env, help): |
| | | PTPShell(banner=help, locals=env) |
| | | def ptpython_shell_runner(env, help): |
| | | print(help) |
| | | return embed(locals=env) |
| | | |
| | | return shell |
| | | .. versionchanged:: 1.6 |
| | | User-defined shells may be registered using entry points. Prior to this |
| | | the only supported shells were ``ipython``, ``bpython`` and ``python``. |
| | | |
| | | ``ipython`` and ``bpython`` have been moved into their respective |
| | | packages ``pyramid_ipython`` and ``pyramid_bpython``. |
| | | |
| | | |
| | | Setting a Default Shell |
| | | ~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | You may use the ``default_shell`` option in your ``[pshell]`` ini section to |
| | | specify a list of preferred shells. |
| | | |
| | | .. code-block:: ini |
| | | :linenos: |
| | | |
| | | [pshell] |
| | | default_shell = ptpython ipython bpython |
| | | |
| | | .. versionadded:: 1.6 |
| | | |
| | | |
| | | .. index:: |
| | | pair: routes; printing |
| | |
| | | |
| | | Displaying All Application Routes |
| | | --------------------------------- |
| | | |
| | | .. seealso:: See also the output of :ref:`proutes --help <proutes_script>`. |
| | | |
| | | You can use the ``proutes`` command in a terminal window to print a summary of |
| | | routes related to your application. Much like the ``pshell`` command (see |
| | |
| | | Displaying "Tweens" |
| | | ------------------- |
| | | |
| | | .. seealso:: See also the output of :ref:`ptweens --help <ptweens_script>`. |
| | | |
| | | A :term:`tween` is a bit of code that sits between the main Pyramid application |
| | | request handler and the WSGI application which calls it. A user can get a |
| | | representation of both the implicit tween ordering (the ordering specified by |
| | |
| | | |
| | | See :ref:`registering_tweens` for more information about tweens. |
| | | |
| | | |
| | | .. index:: |
| | | single: invoking a request |
| | | single: prequest |
| | |
| | | |
| | | Invoking a Request |
| | | ------------------ |
| | | |
| | | .. seealso:: See also the output of :ref:`prequest --help <prequest_script>`. |
| | | |
| | | You can use the ``prequest`` command-line utility to send a request to your |
| | | application and see the response body without starting a server. |
| | |
| | | |
| | | $ $VENV/bin/prequest -mPOST development.ini / < somefile |
| | | |
| | | |
| | | Using Custom Arguments to Python when Running ``p*`` Scripts |
| | | ------------------------------------------------------------ |
| | | |
| | |
| | | |
| | | python -3 -m pyramid.scripts.pserve development.ini |
| | | |
| | | |
| | | .. index:: |
| | | single: pdistreport |
| | | single: distributions, showing installed |
| | | single: showing installed distributions |
| | | |
| | | .. _showing_distributions: |
| | | |
| | | Showing All Installed Distributions and Their Versions |
| | | ------------------------------------------------------ |
| | | |
| | | .. versionadded:: 1.5 |
| | | |
| | | .. seealso:: See also the output of :ref:`pdistreport --help |
| | | <pdistreport_script>`. |
| | | |
| | | You can use the ``pdistreport`` command to show the :app:`Pyramid` version in |
| | | use, the Python version in use, and all installed versions of Python |
| | |
| | | pastebin when you are having problems and need someone with more familiarity |
| | | with Python packaging and distribution than you have to look at your |
| | | environment. |
| | | |
| | | |
| | | .. _writing_a_script: |
| | | |
| | |
| | | object present in the ``env`` dictionary returned by |
| | | :func:`pyramid.paster.bootstrap` will be a :app:`Pyramid` :term:`router`. |
| | | |
| | | |
| | | Changing the Request |
| | | ~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | |
| | | env['request'].route_url('verify', code='1337') |
| | | # will return 'https://example.com/prefix/verify/1337' |
| | | |
| | | |
| | | Cleanup |
| | | ~~~~~~~ |
| | | |
| | |
| | | # .. do stuff ... |
| | | |
| | | env['closer']() |
| | | |
| | | |
| | | Setting Up Logging |
| | | ~~~~~~~~~~~~~~~~~~ |
| | |
| | | See :ref:`logging_chapter` for more information on logging within |
| | | :app:`Pyramid`. |
| | | |
| | | |
| | | .. index:: |
| | | single: console script |
| | | |
| | |
| | | =============================== |
| | | |
| | | Pyramid allows you to extend its Configurator with custom directives. Custom |
| | | directives can use other directives, they can add a custom :term:`action`, |
| | | they can participate in :term:`conflict resolution`, and they can provide |
| | | some number of :term:`introspectable` objects. |
| | | directives can use other directives, they can add a custom :term:`action`, they |
| | | can participate in :term:`conflict resolution`, and they can provide some |
| | | number of :term:`introspectable` objects. |
| | | |
| | | .. index:: |
| | | single: add_directive |
| | |
| | | Adding Methods to the Configurator via ``add_directive`` |
| | | -------------------------------------------------------- |
| | | |
| | | Framework extension writers can add arbitrary methods to a |
| | | :term:`Configurator` by using the |
| | | :meth:`pyramid.config.Configurator.add_directive` method of the configurator. |
| | | Using :meth:`~pyramid.config.Configurator.add_directive` makes it possible to |
| | | extend a Pyramid configurator in arbitrary ways, and allows it to perform |
| | | application-specific tasks more succinctly. |
| | | Framework extension writers can add arbitrary methods to a :term:`Configurator` |
| | | by using the :meth:`pyramid.config.Configurator.add_directive` method of the |
| | | configurator. Using :meth:`~pyramid.config.Configurator.add_directive` makes it |
| | | possible to extend a Pyramid configurator in arbitrary ways, and allows it to |
| | | perform application-specific tasks more succinctly. |
| | | |
| | | The :meth:`~pyramid.config.Configurator.add_directive` method accepts two |
| | | positional arguments: a method name and a callable object. The callable |
| | | object is usually a function that takes the configurator instance as its |
| | | first argument and accepts other arbitrary positional and keyword arguments. |
| | | For example: |
| | | positional arguments: a method name and a callable object. The callable object |
| | | is usually a function that takes the configurator instance as its first |
| | | argument and accepts other arbitrary positional and keyword arguments. For |
| | | example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | add_newrequest_subscriber) |
| | | |
| | | Once :meth:`~pyramid.config.Configurator.add_directive` is called, a user can |
| | | then call the added directive by its given name as if it were a built-in |
| | | method of the Configurator: |
| | | then call the added directive by its given name as if it were a built-in method |
| | | of the Configurator: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | config.add_newrequest_subscriber(mysubscriber) |
| | | |
| | | A call to :meth:`~pyramid.config.Configurator.add_directive` is often |
| | | "hidden" within an ``includeme`` function within a "frameworky" package meant |
| | | to be included as per :ref:`including_configuration` via |
| | | A call to :meth:`~pyramid.config.Configurator.add_directive` is often "hidden" |
| | | within an ``includeme`` function within a "frameworky" package meant to be |
| | | included as per :ref:`including_configuration` via |
| | | :meth:`~pyramid.config.Configurator.include`. For example, if you put this |
| | | code in a package named ``pyramid_subscriberhelpers``: |
| | | |
| | |
| | | config.add_directive('add_newrequest_subscriber', |
| | | add_newrequest_subscriber) |
| | | |
| | | The user of the add-on package ``pyramid_subscriberhelpers`` would then be |
| | | able to install it and subsequently do: |
| | | The user of the add-on package ``pyramid_subscriberhelpers`` would then be able |
| | | to install it and subsequently do: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | If a custom directive can't do its work exclusively in terms of existing |
| | | configurator methods (such as |
| | | :meth:`pyramid.config.Configurator.add_subscriber`, as above), the directive |
| | | may need to make use of the :meth:`pyramid.config.Configurator.action` |
| | | method. This method adds an entry to the list of "actions" that Pyramid will |
| | | attempt to process when :meth:`pyramid.config.Configurator.commit` is called. |
| | | An action is simply a dictionary that includes a :term:`discriminator`, |
| | | possibly a callback function, and possibly other metadata used by Pyramid's |
| | | action system. |
| | | :meth:`pyramid.config.Configurator.add_subscriber` as above), the directive may |
| | | need to make use of the :meth:`pyramid.config.Configurator.action` method. This |
| | | method adds an entry to the list of "actions" that Pyramid will attempt to |
| | | process when :meth:`pyramid.config.Configurator.commit` is called. An action is |
| | | simply a dictionary that includes a :term:`discriminator`, possibly a callback |
| | | function, and possibly other metadata used by Pyramid's action system. |
| | | |
| | | Here's an example directive which uses the "action" method: |
| | | |
| | |
| | | |
| | | When the :meth:`~pyramid.config.Configurator.action` method is called, it |
| | | appends an action to the list of pending configuration actions. All pending |
| | | actions with the same discriminator value are potentially in conflict with |
| | | one another (see :ref:`conflict_detection`). When the |
| | | actions with the same discriminator value are potentially in conflict with one |
| | | another (see :ref:`conflict_detection`). When the |
| | | :meth:`~pyramid.config.Configurator.commit` method of the Configurator is |
| | | called (either explicitly or as the result of calling |
| | | :meth:`~pyramid.config.Configurator.make_wsgi_app`), conflicting actions are |
| | | potentially automatically resolved as per |
| | | :ref:`automatic_conflict_resolution`. If a conflict cannot be automatically |
| | | resolved, a :exc:`pyramid.exceptions.ConfigurationConflictError` is raised |
| | | and application startup is prevented. |
| | | potentially automatically resolved as per :ref:`automatic_conflict_resolution`. |
| | | If a conflict cannot be automatically resolved, a |
| | | :exc:`pyramid.exceptions.ConfigurationConflictError` is raised and application |
| | | startup is prevented. |
| | | |
| | | In our above example, therefore, if a consumer of our ``add_jammyjam`` |
| | | directive did this: |
| | |
| | | resolution cannot resolve the conflict (because no ``config.include`` is |
| | | involved), and the user provided no intermediate |
| | | :meth:`pyramid.config.Configurator.commit` call between the calls to |
| | | ``add_jammyjam`` to ensure that the successive calls did not conflict with |
| | | each other. |
| | | ``add_jammyjam`` to ensure that the successive calls did not conflict with each |
| | | other. |
| | | |
| | | This demonstrates the purpose of the discriminator argument to the action |
| | | method: it's used to indicate a uniqueness constraint for an action. Two |
| | | actions with the same discriminator will conflict unless the conflict is |
| | | automatically or manually resolved. A discriminator can be any hashable |
| | | object, but it is generally a string or a tuple. *You use a discriminator to |
| | | automatically or manually resolved. A discriminator can be any hashable object, |
| | | but it is generally a string or a tuple. *You use a discriminator to |
| | | declaratively ensure that the user doesn't provide ambiguous configuration |
| | | statements.* |
| | | |
| | |
| | | are processed during :meth:`~pyramid.config.Configurator.commit`, and no |
| | | conflicts occur, the *callable* provided as the second argument to the |
| | | :meth:`~pyramid.config.Configurator.action` method within ``add_jammyjam`` is |
| | | called with no arguments. The callable in ``add_jammyjam`` is the |
| | | ``register`` closure function. It simply sets the value |
| | | ``config.registry.jammyjam`` to whatever the user passed in as the |
| | | ``jammyjam`` argument to the ``add_jammyjam`` function. Therefore, the |
| | | result of the user's call to our directive will set the ``jammyjam`` |
| | | attribute of the registry to the string ``first``. *A callable is used by a |
| | | directive to defer the result of a user's call to the directive until |
| | | conflict detection has had a chance to do its job*. |
| | | called with no arguments. The callable in ``add_jammyjam`` is the ``register`` |
| | | closure function. It simply sets the value ``config.registry.jammyjam`` to |
| | | whatever the user passed in as the ``jammyjam`` argument to the |
| | | ``add_jammyjam`` function. Therefore, the result of the user's call to our |
| | | directive will set the ``jammyjam`` attribute of the registry to the string |
| | | ``first``. *A callable is used by a directive to defer the result of a user's |
| | | call to the directive until conflict detection has had a chance to do its job*. |
| | | |
| | | Other arguments exist to the :meth:`~pyramid.config.Configurator.action` |
| | | method, including ``args``, ``kw``, ``order``, and ``introspectables``. |
| | | method, including ``args``, ``kw``, ``order``, and ``introspectables``. |
| | | |
| | | ``args`` and ``kw`` exist as values, which, if passed, will be used as |
| | | arguments to the ``callable`` function when it is called back. For example |
| | | our directive might use them like so: |
| | | ``args`` and ``kw`` exist as values, which if passed will be used as arguments |
| | | to the ``callable`` function when it is called back. For example, our |
| | | directive might use them like so: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | In the above example, when this directive is used to generate an action, and |
| | | that action is committed, ``config.registry.jammyjam_args`` will be set to |
| | | ``('one',)`` and ``config.registry.jammyjam_kw`` will be set to |
| | | ``{'two':'two'}``. ``args`` and ``kw`` are honestly not very useful when |
| | | your ``callable`` is a closure function, because you already usually have |
| | | access to every local in the directive without needing them to be passed |
| | | back. They can be useful, however, if you don't use a closure as a callable. |
| | | ``{'two':'two'}``. ``args`` and ``kw`` are honestly not very useful when your |
| | | ``callable`` is a closure function, because you already usually have access to |
| | | every local in the directive without needing them to be passed back. They can |
| | | be useful, however, if you don't use a closure as a callable. |
| | | |
| | | ``order`` is a crude order control mechanism. ``order`` defaults to the |
| | | integer ``0``; it can be set to any other integer. All actions that share an |
| | | order will be called before other actions that share a higher order. This |
| | | makes it possible to write a directive with callable logic that relies on the |
| | | execution of the callable of another directive being done first. For |
| | | example, Pyramid's :meth:`pyramid.config.Configurator.add_view` directive |
| | | registers an action with a higher order than the |
| | | execution of the callable of another directive being done first. For example, |
| | | Pyramid's :meth:`pyramid.config.Configurator.add_view` directive registers an |
| | | action with a higher order than the |
| | | :meth:`pyramid.config.Configurator.add_route` method. Due to this, the |
| | | ``add_view`` method's callable can assume that, if a ``route_name`` was |
| | | passed to it, that a route by this name was already registered by |
| | | ``add_route``, and if such a route has not already been registered, it's a |
| | | configuration error (a view that names a nonexistent route via its |
| | | ``route_name`` parameter will never be called). As of Pyramid 1.6 it is |
| | | possible for one action to invoke another. See :ref:`ordering_actions` for |
| | | more information. |
| | | ``add_view`` method's callable can assume that, if a ``route_name`` was passed |
| | | to it, that a route by this name was already registered by ``add_route``, and |
| | | if such a route has not already been registered, it's a configuration error (a |
| | | view that names a nonexistent route via its ``route_name`` parameter will never |
| | | be called). |
| | | |
| | | ``introspectables`` is a sequence of :term:`introspectable` objects. You can |
| | | pass a sequence of introspectables to the |
| | | :meth:`~pyramid.config.Configurator.action` method, which allows you to |
| | | augment Pyramid's configuration introspection system. |
| | | .. versionchanged:: 1.6 |
| | | As of Pyramid 1.6 it is possible for one action to invoke another. See |
| | | :ref:`ordering_actions` for more information. |
| | | |
| | | Finally, ``introspectables`` is a sequence of :term:`introspectable` objects. |
| | | You can pass a sequence of introspectables to the |
| | | :meth:`~pyramid.config.Configurator.action` method, which allows you to augment |
| | | Pyramid's configuration introspection system. |
| | | |
| | | .. _ordering_actions: |
| | | |
| | |
| | | actions. The logic within actions is deferred until a call to |
| | | :meth:`pyramid.config.Configurator.commit` (which is automatically invoked by |
| | | :meth:`pyramid.config.Configurator.make_wsgi_app`). This means you may call |
| | | ``config.add_view(route_name='foo')`` **before** |
| | | ``config.add_route('foo', '/foo')`` because nothing actually happens until |
| | | commit-time. During a commit cycle conflicts are resolved, actions are ordered |
| | | and executed. |
| | | ``config.add_view(route_name='foo')`` **before** ``config.add_route('foo', |
| | | '/foo')`` because nothing actually happens until commit-time. During a commit |
| | | cycle, conflicts are resolved, and actions are ordered and executed. |
| | | |
| | | By default, almost every action in Pyramid has an ``order`` of |
| | | :const:`pyramid.config.PHASE3_CONFIG`. Every action within the same order-level |
| | | will be executed in the order it was called. |
| | | This means that if an action must be reliably executed before or after another |
| | | action, the ``order`` must be defined explicitly to make this work. For |
| | | example, views are dependent on routes being defined. Thus the action created |
| | | by :meth:`pyramid.config.Configurator.add_route` has an ``order`` of |
| | | will be executed in the order it was called. This means that if an action must |
| | | be reliably executed before or after another action, the ``order`` must be |
| | | defined explicitly to make this work. For example, views are dependent on |
| | | routes being defined. Thus the action created by |
| | | :meth:`pyramid.config.Configurator.add_route` has an ``order`` of |
| | | :const:`pyramid.config.PHASE2_CONFIG`. |
| | | |
| | | Pre-defined Phases |
| | |
| | | |
| | | :const:`pyramid.config.PHASE0_CONFIG` |
| | | |
| | | - This phase is reserved for developers who want to execute actions prior |
| | | to Pyramid's core directives. |
| | | - This phase is reserved for developers who want to execute actions prior to |
| | | Pyramid's core directives. |
| | | |
| | | :const:`pyramid.config.PHASE1_CONFIG` |
| | | |
| | |
| | | |
| | | - The default for all builtin or custom directives unless otherwise specified. |
| | | |
| | | Calling Actions From Actions |
| | | Calling Actions from Actions |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | .. versionadded:: 1.6 |
| | | |
| | | Pyramid's configurator allows actions to be added during a commit-cycle as |
| | | long as they are added to the current or a later ``order`` phase. This means |
| | | that your custom action can defer decisions until commit-time and then do |
| | | things like invoke :meth:`pyramid.config.Configurator.add_route`. It can also |
| | | provide better conflict detection if your addon needs to call more than one |
| | | other action. |
| | | Pyramid's configurator allows actions to be added during a commit-cycle as long |
| | | as they are added to the current or a later ``order`` phase. This means that |
| | | your custom action can defer decisions until commit-time and then do things |
| | | like invoke :meth:`pyramid.config.Configurator.add_route`. It can also provide |
| | | better conflict detection if your addon needs to call more than one other |
| | | action. |
| | | |
| | | For example, let's make an addon that invokes ``add_route`` and ``add_view``, |
| | | but we want it to conflict with any other call to our addon: |
| | |
| | | config.action(('auto route', name), register, order=PHASE0_CONFIG) |
| | | |
| | | Now someone else can use your addon and be informed if there is a conflict |
| | | between this route and another, or two calls to ``add_auto_route``. |
| | | Notice how we had to invoke our action **before** ``add_view`` or |
| | | ``add_route``. If we tried to invoke this afterward, the subsequent calls to |
| | | ``add_view`` and ``add_route`` would cause conflicts because that phase had |
| | | already been executed, and the configurator cannot go back in time to add more |
| | | views during that commit-cycle. |
| | | between this route and another, or two calls to ``add_auto_route``. Notice how |
| | | we had to invoke our action **before** ``add_view`` or ``add_route``. If we |
| | | tried to invoke this afterward, the subsequent calls to ``add_view`` and |
| | | ``add_route`` would cause conflicts because that phase had already been |
| | | executed, and the configurator cannot go back in time to add more views during |
| | | that commit-cycle. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | introspectables when called. For example, when you register a view via |
| | | ``add_view``, the directive registers at least one introspectable: an |
| | | introspectable about the view registration itself, providing human-consumable |
| | | values for the arguments it was passed. You can later use the introspection |
| | | query system to determine whether a particular view uses a renderer, or |
| | | whether a particular view is limited to a particular request method, or which |
| | | routes a particular view is registered against. The Pyramid "debug toolbar" |
| | | makes use of the introspection system in various ways to display information |
| | | to Pyramid developers. |
| | | values for the arguments passed into it. You can later use the introspection |
| | | query system to determine whether a particular view uses a renderer, or whether |
| | | a particular view is limited to a particular request method, or against which |
| | | routes a particular view is registered. The Pyramid "debug toolbar" makes use |
| | | of the introspection system in various ways to display information to Pyramid |
| | | developers. |
| | | |
| | | Introspection values are set when a sequence of :term:`introspectable` |
| | | objects is passed to the :meth:`~pyramid.config.Configurator.action` method. |
| | | Here's an example of a directive which uses introspectables: |
| | | Introspection values are set when a sequence of :term:`introspectable` objects |
| | | is passed to the :meth:`~pyramid.config.Configurator.action` method. Here's an |
| | | example of a directive which uses introspectables: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | config.add_directive('add_jammyjam', add_jammyjam) |
| | | |
| | | If you notice, the above directive uses the ``introspectable`` attribute of a |
| | | Configurator (:attr:`pyramid.config.Configurator.introspectable`) to create |
| | | an introspectable object. The introspectable object's constructor requires |
| | | at least four arguments: the ``category_name``, the ``discriminator``, the |
| | | Configurator (:attr:`pyramid.config.Configurator.introspectable`) to create an |
| | | introspectable object. The introspectable object's constructor requires at |
| | | least four arguments: the ``category_name``, the ``discriminator``, the |
| | | ``title``, and the ``type_name``. |
| | | |
| | | The ``category_name`` is a string representing the logical category for this |
| | |
| | | within its category for sorting and presentation purposes. It can be any |
| | | value. |
| | | |
| | | An introspectable is also dictionary-like. It can contain any set of |
| | | key/value pairs, typically related to the arguments passed to its related |
| | | directive. While the category_name, discriminator, title and type_name are |
| | | *metadata* about the introspectable, the values provided as key/value pairs |
| | | An introspectable is also dictionary-like. It can contain any set of key/value |
| | | pairs, typically related to the arguments passed to its related directive. |
| | | While the ``category_name``, ``discriminator``, ``title``, and ``type_name`` |
| | | are *metadata* about the introspectable, the values provided as key/value pairs |
| | | are the actual data provided by the introspectable. In the above example, we |
| | | set the ``value`` key to the value of the ``value`` argument passed to the |
| | | directive. |
| | | |
| | | Our directive above mutates the introspectable, and passes it in to the |
| | | ``action`` method as the first element of a tuple as the value of the |
| | | ``introspectable`` keyword argument. This associates this introspectable |
| | | with the action. Introspection tools will then display this introspectable |
| | | in their index. |
| | | ``introspectable`` keyword argument. This associates this introspectable with |
| | | the action. Introspection tools will then display this introspectable in their |
| | | index. |
| | | |
| | | Introspectable Relationships |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | |
| | | config.add_directive('add_jammyjam', add_jammyjam) |
| | | |
| | | In the above example, the ``add_jammyjam`` directive registers two |
| | | introspectables. The first is related to the ``value`` passed to the |
| | | directive; the second is related to the ``template`` passed to the directive. |
| | | If you believe a concept within a directive is important enough to have its |
| | | own introspectable, you can cause the same directive to register more than |
| | | one introspectable, registering one introspectable for the "main idea" and |
| | | another for a related concept. |
| | | introspectables: the first is related to the ``value`` passed to the directive, |
| | | and the second is related to the ``template`` passed to the directive. If you |
| | | believe a concept within a directive is important enough to have its own |
| | | introspectable, you can cause the same directive to register more than one |
| | | introspectable, registering one introspectable for the "main idea" and another |
| | | for a related concept. |
| | | |
| | | The call to ``intr.relate`` above |
| | | (:meth:`pyramid.interfaces.IIntrospectable.relate`) is passed two arguments: |
| | | a category name and a directive. The example above effectively indicates |
| | | that the directive wishes to form a relationship between the ``intr`` |
| | | introspectable and the ``tmpl_intr`` introspectable; the arguments passed to |
| | | ``relate`` are the category name and discriminator of the ``tmpl_intr`` |
| | | introspectable. |
| | | (:meth:`pyramid.interfaces.IIntrospectable.relate`) is passed two arguments: a |
| | | category name and a directive. The example above effectively indicates that |
| | | the directive wishes to form a relationship between the ``intr`` introspectable |
| | | and the ``tmpl_intr`` introspectable; the arguments passed to ``relate`` are |
| | | the category name and discriminator of the ``tmpl_intr`` introspectable. |
| | | |
| | | Relationships need not be made between two introspectables created by the |
| | | same directive. Instead, a relationship can be formed between an |
| | | introspectable created in one directive and another introspectable created in |
| | | another by calling ``relate`` on either side with the other directive's |
| | | category name and discriminator. An error will be raised at configuration |
| | | commit time if you attempt to relate an introspectable with another |
| | | nonexistent introspectable, however. |
| | | Relationships need not be made between two introspectables created by the same |
| | | directive. Instead a relationship can be formed between an introspectable |
| | | created in one directive and another introspectable created in another by |
| | | calling ``relate`` on either side with the other directive's category name and |
| | | discriminator. An error will be raised at configuration commit time if you |
| | | attempt to relate an introspectable with another nonexistent introspectable, |
| | | however. |
| | | |
| | | Introspectable relationships will show up in frontend system renderings of |
| | | introspection values. For example, if a view registration names a route |
| | | name, the introspectable related to the view callable will show a reference |
| | | to the route to which it relates to and vice versa. |
| | | introspection values. For example, if a view registration names a route name, |
| | | the introspectable related to the view callable will show a reference to the |
| | | route to which it relates and vice versa. |
| | |
| | | .. _extending_chapter: |
| | | |
| | | Extending An Existing :app:`Pyramid` Application |
| | | =================================================== |
| | | Extending an Existing :app:`Pyramid` Application |
| | | ================================================ |
| | | |
| | | If a :app:`Pyramid` developer has obeyed certain constraints while building |
| | | an application, a third party should be able to change the application's |
| | | behavior without needing to modify its source code. The behavior of a |
| | | :app:`Pyramid` application that obeys certain constraints can be *overridden* |
| | | or *extended* without modification. |
| | | If a :app:`Pyramid` developer has obeyed certain constraints while building an |
| | | application, a third party should be able to change the application's behavior |
| | | without needing to modify its source code. The behavior of a :app:`Pyramid` |
| | | application that obeys certain constraints can be *overridden* or *extended* |
| | | without modification. |
| | | |
| | | We'll define some jargon here for the benefit of identifying the parties |
| | | involved in such an effort. |
| | |
| | | The original application developer. |
| | | |
| | | Integrator |
| | | Another developer who wishes to reuse the application written by the |
| | | original application developer in an unanticipated context. He may also |
| | | wish to modify the original application without changing the original |
| | | application's source code. |
| | | Another developer who wishes to reuse the application written by the original |
| | | application developer in an unanticipated context. They may also wish to |
| | | modify the original application without changing the original application's |
| | | source code. |
| | | |
| | | The Difference Between "Extensible" and "Pluggable" Applications |
| | | ---------------------------------------------------------------- |
| | |
| | | Other web frameworks, such as :term:`Django`, advertise that they allow |
| | | developers to create "pluggable applications". They claim that if you create |
| | | an application in a certain way, it will be integratable in a sensible, |
| | | structured way into another arbitrarily-written application or project |
| | | created by a third-party developer. |
| | | structured way into another arbitrarily-written application or project created |
| | | by a third-party developer. |
| | | |
| | | :app:`Pyramid`, as a platform, does not claim to provide such a feature. The |
| | | platform provides no guarantee that you can create an application and package |
| | | it up such that an arbitrary integrator can use it as a subcomponent in a |
| | | larger Pyramid application or project. Pyramid does not mandate the |
| | | constraints necessary for such a pattern to work satisfactorily. Because |
| | | Pyramid is not very "opinionated", developers are able to use wildly |
| | | different patterns and technologies to build an application. A given Pyramid |
| | | application may happen to be reusable by a particular third party integrator, |
| | | because the integrator and the original developer may share similar base |
| | | technology choices (such as the use of a particular relational database or |
| | | ORM). But the same application may not be reusable by a different developer, |
| | | because he has made different technology choices which are incompatible with |
| | | the original developer's. |
| | | Pyramid is not very "opinionated", developers are able to use wildly different |
| | | patterns and technologies to build an application. A given Pyramid application |
| | | may happen to be reusable by a particular third party integrator because the |
| | | integrator and the original developer may share similar base technology choices |
| | | (such as the use of a particular relational database or ORM). But the same |
| | | application may not be reusable by a different developer, because they have |
| | | made different technology choices which are incompatible with the original |
| | | developer's. |
| | | |
| | | As a result, the concept of a "pluggable application" is left to layers built |
| | | above Pyramid, such as a "CMS" layer or "application server" layer. Such |
| | | layers are apt to provide the necessary "opinions" (such as mandating a |
| | | storage layer, a templating system, and a structured, well-documented pattern |
| | | of registering that certain URLs map to certain bits of code) which makes the |
| | | concept of a "pluggable application" possible. "Pluggable applications", |
| | | thus, should not plug into Pyramid itself but should instead plug into a |
| | | system written atop Pyramid. |
| | | layers are apt to provide the necessary "opinions" (such as mandating a storage |
| | | layer, a templating system, and a structured, well-documented pattern of |
| | | registering that certain URLs map to certain bits of code) which makes the |
| | | concept of a "pluggable application" possible. "Pluggable applications", thus, |
| | | should not plug into Pyramid itself but should instead plug into a system |
| | | written atop Pyramid. |
| | | |
| | | Although it does not provide for "pluggable applications", Pyramid *does* |
| | | provide a rich set of mechanisms which allows for the extension of a single |
| | |
| | | |
| | | .. _building_an_extensible_app: |
| | | |
| | | Rules for Building An Extensible Application |
| | | Rules for Building an Extensible Application |
| | | -------------------------------------------- |
| | | |
| | | There is only one rule you need to obey if you want to build a maximally |
| | | extensible :app:`Pyramid` application: as a developer, you should factor any |
| | | overrideable :term:`imperative configuration` you've created into functions |
| | | which can be used via :meth:`pyramid.config.Configurator.include` rather than |
| | | overridable :term:`imperative configuration` you've created into functions |
| | | which can be used via :meth:`pyramid.config.Configurator.include`, rather than |
| | | inlined as calls to methods of a :term:`Configurator` within the ``main`` |
| | | function in your application's ``__init__.py``. For example, rather than: |
| | | |
| | |
| | | config.add_view('myapp.views.view1', name='view1') |
| | | config.add_view('myapp.views.view2', name='view2') |
| | | |
| | | You should move the calls to ``add_view`` outside of the (non-reusable) |
| | | ``if __name__ == '__main__'`` block, and into a reusable function: |
| | | You should move the calls to ``add_view`` outside of the (non-reusable) ``if |
| | | __name__ == '__main__'`` block, and into a reusable function: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | config.add_view('myapp.views.view1', name='view1') |
| | | config.add_view('myapp.views.view2', name='view2') |
| | | |
| | | Doing this allows an integrator to maximally reuse the configuration |
| | | statements that relate to your application by allowing him to selectively |
| | | include or disinclude the configuration functions you've created from an |
| | | "override package". |
| | | Doing this allows an integrator to maximally reuse the configuration statements |
| | | that relate to your application by allowing them to selectively include or |
| | | exclude the configuration functions you've created from an "override package". |
| | | |
| | | Alternately, you can use :term:`ZCML` for the purpose of making configuration |
| | | extensible and overrideable. :term:`ZCML` declarations that belong to an |
| | | Alternatively you can use :term:`ZCML` for the purpose of making configuration |
| | | extensible and overridable. :term:`ZCML` declarations that belong to an |
| | | application can be overridden and extended by integrators as necessary in a |
| | | similar fashion. If you use only :term:`ZCML` to configure your application, |
| | | it will automatically be maximally extensible without any manual effort. See |
| | |
| | | Fundamental Plugpoints |
| | | ~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | The fundamental "plug points" of an application developed using |
| | | :app:`Pyramid` are *routes*, *views*, and *assets*. Routes are declarations |
| | | made using the :meth:`pyramid.config.Configurator.add_route` method. Views |
| | | are declarations made using the :meth:`pyramid.config.Configurator.add_view` |
| | | method. Assets are files that are |
| | | accessed by :app:`Pyramid` using the :term:`pkg_resources` API such as static |
| | | files and templates via a :term:`asset specification`. Other directives and |
| | | configurator methods also deal in routes, views, and assets. For example, the |
| | | ``add_handler`` directive of the ``pyramid_handlers`` package adds a single |
| | | route, and some number of views. |
| | | The fundamental "plug points" of an application developed using :app:`Pyramid` |
| | | are *routes*, *views*, and *assets*. Routes are declarations made using the |
| | | :meth:`pyramid.config.Configurator.add_route` method. Views are declarations |
| | | made using the :meth:`pyramid.config.Configurator.add_view` method. Assets are |
| | | files that are accessed by :app:`Pyramid` using the :term:`pkg_resources` API |
| | | such as static files and templates via a :term:`asset specification`. Other |
| | | directives and configurator methods also deal in routes, views, and assets. |
| | | For example, the ``add_handler`` directive of the ``pyramid_handlers`` package |
| | | adds a single route and some number of views. |
| | | |
| | | .. index:: |
| | | single: extending an existing application |
| | |
| | | --------------------------------- |
| | | |
| | | The steps for extending an existing application depend largely on whether the |
| | | application does or does not use configuration decorators and/or imperative |
| | | code. |
| | | application does or does not use configuration decorators or imperative code. |
| | | |
| | | If The Application Has Configuration Decorations |
| | | If the Application Has Configuration Decorations |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | You've inherited a :app:`Pyramid` application which you'd like to extend or |
| | |
| | | config.add_view('mypackage.views.myview', name='myview') |
| | | |
| | | If you want to *override* configuration in the application, you *may* need to |
| | | run :meth:`pyramid.config.Configurator.commit` after performing the scan of |
| | | the original package, then add additional configuration that registers more |
| | | views or routes which performs overrides. |
| | | run :meth:`pyramid.config.Configurator.commit` after performing the scan of the |
| | | original package, then add additional configuration that registers more views |
| | | or routes which perform overrides. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | Once this is done, you should be able to extend or override the application |
| | | like any other (see :ref:`extending_the_application`). |
| | | |
| | | You can alternately just prevent a :term:`scan` from happening (by omitting |
| | | any call to the :meth:`pyramid.config.Configurator.scan` method). This will |
| | | You can alternatively just prevent a :term:`scan` from happening by omitting |
| | | any call to the :meth:`pyramid.config.Configurator.scan` method. This will |
| | | cause the decorators attached to objects in the target application to do |
| | | nothing. At this point, you will need to convert all the configuration done |
| | | in decorators into equivalent imperative configuration or ZCML and add that |
| | | nothing. At this point, you will need to convert all the configuration done in |
| | | decorators into equivalent imperative configuration or ZCML, and add that |
| | | configuration or ZCML to a separate Python package as described in |
| | | :ref:`extending_the_application`. |
| | | |
| | |
| | | Extending the Application |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | To extend or override the behavior of an existing application, you will need |
| | | to create a new package which includes the configuration of the old package, |
| | | and you'll perhaps need to create implementations of the types of things |
| | | you'd like to override (such as views), which are referred to within the |
| | | original package. |
| | | To extend or override the behavior of an existing application, you will need to |
| | | create a new package which includes the configuration of the old package, and |
| | | you'll perhaps need to create implementations of the types of things you'd like |
| | | to override (such as views), to which they are referred within the original |
| | | package. |
| | | |
| | | The general pattern for extending an existing application looks something |
| | | like this: |
| | | The general pattern for extending an existing application looks something like |
| | | this: |
| | | |
| | | - Create a new Python package. The easiest way to do this is to create a new |
| | | :app:`Pyramid` application using the scaffold mechanism. See |
| | | :ref:`creating_a_project` for more information. |
| | | |
| | | - In the new package, create Python files containing views and other |
| | | overridden elements, such as templates and static assets as necessary. |
| | | - In the new package, create Python files containing views and other overridden |
| | | elements, such as templates and static assets as necessary. |
| | | |
| | | - Install the new package into the same Python environment as the original |
| | | application (e.g. ``$VENV/bin/python setup.py develop`` or |
| | | application (e.g., ``$VENV/bin/python setup.py develop`` or |
| | | ``$VENV/bin/python setup.py install``). |
| | | |
| | | - Change the ``main`` function in the new package's ``__init__.py`` to include |
| | | the original :app:`Pyramid` application's configuration functions via |
| | | :meth:`pyramid.config.Configurator.include` statements or a :term:`scan`. |
| | | |
| | | - Wire the new views and assets created in the new package up using |
| | | imperative registrations within the ``main`` function of the |
| | | ``__init__.py`` file of the new application. This wiring should happen |
| | | *after* including the configuration functions of the old application. |
| | | These registrations will extend or override any registrations performed by |
| | | the original application. See :ref:`overriding_views`, |
| | | :ref:`overriding_routes` and :ref:`overriding_resources`. |
| | | - Wire the new views and assets created in the new package up using imperative |
| | | registrations within the ``main`` function of the ``__init__.py`` file of the |
| | | new application. This wiring should happen *after* including the |
| | | configuration functions of the old application. These registrations will |
| | | extend or override any registrations performed by the original application. |
| | | See :ref:`overriding_views`, :ref:`overriding_routes`, and |
| | | :ref:`overriding_resources`. |
| | | |
| | | .. index:: |
| | | pair: overriding; views |
| | |
| | | .. _overriding_views: |
| | | |
| | | Overriding Views |
| | | ~~~~~~~~~~~~~~~~~ |
| | | ~~~~~~~~~~~~~~~~ |
| | | |
| | | The :term:`view configuration` declarations you make which *override* |
| | | The :term:`view configuration` declarations that you make which *override* |
| | | application behavior will usually have the same :term:`view predicate` |
| | | attributes as the original you wish to override. These ``<view>`` |
| | | declarations will point at "new" view code, in the override package you've |
| | | created. The new view code itself will usually be cut-n-paste copies of view |
| | | callables from the original application with slight tweaks. |
| | | attributes as the original that you wish to override. These ``<view>`` |
| | | declarations will point at "new" view code in the override package that you've |
| | | created. The new view code itself will usually be copy-and-paste copies of |
| | | view callables from the original application with slight tweaks. |
| | | |
| | | For example, if the original application has the following |
| | | ``configure_views`` configuration method: |
| | | For example, if the original application has the following ``configure_views`` |
| | | configuration method: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | config.include(configure_views) |
| | | config.add_view('theoverrideapp.views.theview', name='theview') |
| | | |
| | | In this case, the ``theoriginalapp.views.theview`` view will never be |
| | | executed. Instead, a new view, ``theoverrideapp.views.theview`` will be |
| | | executed instead, when request circumstances dictate. |
| | | In this case, the ``theoriginalapp.views.theview`` view will never be executed. |
| | | Instead, a new view, ``theoverrideapp.views.theview`` will be executed when |
| | | request circumstances dictate. |
| | | |
| | | A similar pattern can be used to *extend* the application with ``add_view`` |
| | | declarations. Just register a new view against some other set of predicates |
| | | to make sure the URLs it implies are available on some other page rendering. |
| | | declarations. Just register a new view against some other set of predicates to |
| | | make sure the URLs it implies are available on some other page rendering. |
| | | |
| | | .. index:: |
| | | pair: overriding; routes |
| | |
| | | Overriding Routes |
| | | ~~~~~~~~~~~~~~~~~ |
| | | |
| | | Route setup is currently typically performed in a sequence of ordered calls |
| | | to :meth:`~pyramid.config.Configurator.add_route`. Because these calls are |
| | | Route setup is currently typically performed in a sequence of ordered calls to |
| | | :meth:`~pyramid.config.Configurator.add_route`. Because these calls are |
| | | ordered relative to each other, and because this ordering is typically |
| | | important, you should retain their relative ordering when performing an |
| | | override. Typically, this means *copying* all the ``add_route`` statements |
| | | into the override package's file and changing them as necessary. Then |
| | | disinclude any ``add_route`` statements from the original application. |
| | | override. Typically this means *copying* all the ``add_route`` statements into |
| | | the override package's file and changing them as necessary. Then exclude any |
| | | ``add_route`` statements from the original application. |
| | | |
| | | .. index:: |
| | | pair: overriding; assets |
| | |
| | | |
| | | Assets are files on the filesystem that are accessible within a Python |
| | | *package*. An entire chapter is devoted to assets: :ref:`assets_chapter`. |
| | | Within this chapter is a section named :ref:`overriding_assets_section`. |
| | | This section of that chapter describes in detail how to override package |
| | | assets with other assets by using the |
| | | :meth:`pyramid.config.Configurator.override_asset` method. Add such |
| | | ``override_asset`` calls to your override package's ``__init__.py`` to |
| | | perform overrides. |
| | | Within this chapter is a section named :ref:`overriding_assets_section`. This |
| | | section of that chapter describes in detail how to override package assets with |
| | | other assets by using the :meth:`pyramid.config.Configurator.override_asset` |
| | | method. Add such ``override_asset`` calls to your override package's |
| | | ``__init__.py`` to perform overrides. |
| | |
| | | .. _hello_traversal_chapter: |
| | | |
| | | Hello Traversal World |
| | | ====================== |
| | | |
| | | ===================== |
| | | |
| | | .. index:: |
| | | single: traversal quick example |
| | | |
| | | Traversal is an alternative to URL dispatch which allows Pyramid |
| | | applications to map URLs to code. |
| | | Traversal is an alternative to URL dispatch which allows Pyramid applications |
| | | to map URLs to code. |
| | | |
| | | If code speaks louder than words, maybe this will help. Here is a |
| | | single-file Pyramid application that uses traversal: |
| | | If code speaks louder than words, maybe this will help. Here is a single-file |
| | | Pyramid application that uses traversal: |
| | | |
| | | .. literalinclude:: hellotraversal.py |
| | | :linenos: |
| | | |
| | | You may notice that this application is intentionally very similar to |
| | | the "hello world" app from :doc:`firstapp`. |
| | | You may notice that this application is intentionally very similar to the |
| | | "hello world" application from :doc:`firstapp`. |
| | | |
| | | On lines 5-6, we create a trivial :term:`resource` class that's just a |
| | | dictionary subclass. |
| | | |
| | | On lines 8-9, we hard-code a :term:`resource tree` in our :term:`root |
| | | factory` function. |
| | | On lines 8-9, we hard-code a :term:`resource tree` in our :term:`root factory` |
| | | function. |
| | | |
| | | On lines 11-13 we define a single :term:`view callable` that can |
| | | display a single instance of our Resource class, passed as the |
| | | ``context`` argument. |
| | | On lines 11-13, we define a single :term:`view callable` that can display a |
| | | single instance of our ``Resource`` class, passed as the ``context`` argument. |
| | | |
| | | The rest of the file sets up and serves our pyramid WSGI app. Line 18 |
| | | is where our view gets configured for use whenever the traversal ends |
| | | with an instance of our Resource class. |
| | | The rest of the file sets up and serves our :app:`Pyramid` WSGI app. Line 18 |
| | | is where our view gets configured for use whenever the traversal ends with an |
| | | instance of our ``Resource`` class. |
| | | |
| | | Interestingly, there are no URLs explicitly configured in this |
| | | application. Instead, the URL space is defined entirely by the keys in |
| | | the resource tree. |
| | | Interestingly, there are no URLs explicitly configured in this application. |
| | | Instead, the URL space is defined entirely by the keys in the resource tree. |
| | | |
| | | Example requests |
| | | ---------------- |
| | | |
| | | If this example is running on http://localhost:8080, and the user |
| | | browses to http://localhost:8080/a/b, Pyramid will call |
| | | ``get_root(request)`` to get the root resource, then traverse the tree |
| | | from there by key; starting from the root, it will find the child with |
| | | key ``"a"``, then its child with key ``"b"``; then use that as the |
| | | ``context`` argument for calling ``hello_world_of_resources``. |
| | | If this example is running on http://localhost:8080, and the user browses to |
| | | http://localhost:8080/a/b, Pyramid will call ``get_root(request)`` to get the |
| | | root resource, then traverse the tree from there by key; starting from the |
| | | root, it will find the child with key ``"a"``, then its child with key ``"b"``; |
| | | then use that as the ``context`` argument for calling |
| | | ``hello_world_of_resources``. |
| | | |
| | | Or, if the user browses to http://localhost:8080/ , Pyramid will |
| | | stop at the root - the outermost Resource instance, in this case - and |
| | | use that as the ``context`` argument to the same view. |
| | | Or, if the user browses to http://localhost:8080/, Pyramid will stop at the |
| | | root—the outermost ``Resource`` instance, in this case—and use that as the |
| | | ``context`` argument to the same view. |
| | | |
| | | Or, if the user browses to a key that doesn't exist in this resource |
| | | tree, like http://localhost:8080/xyz or |
| | | http://localhost:8080/a/b/c/d, the traversal will end by raising a |
| | | KeyError, and Pyramid will turn that into a 404 HTTP response. |
| | | Or, if the user browses to a key that doesn't exist in this resource tree, like |
| | | http://localhost:8080/xyz or http://localhost:8080/a/b/c/d, the traversal will |
| | | end by raising a KeyError, and Pyramid will turn that into a 404 HTTP response. |
| | | |
| | | A more complicated application could have many types of resources, |
| | | with different view callables defined for each type, and even multiple |
| | | views for each type. |
| | | A more complicated application could have many types of resources, with |
| | | different view callables defined for each type, and even multiple views for |
| | | each type. |
| | | |
| | | .. seealso:: |
| | | |
| | |
| | | |
| | | For more about *why* you might use traversal, see |
| | | :doc:`muchadoabouttraversal`. |
| | | |
| | |
| | | Changing the Not Found View |
| | | --------------------------- |
| | | |
| | | When :app:`Pyramid` can't map a URL to view code, it invokes a :term:`Not |
| | | Found View`, which is a :term:`view callable`. The default Not Found View |
| | | can be overridden through application configuration. |
| | | When :app:`Pyramid` can't map a URL to view code, it invokes a :term:`Not Found |
| | | View`, which is a :term:`view callable`. The default Not Found View can be |
| | | overridden through application configuration. |
| | | |
| | | If your application uses :term:`imperative configuration`, you can replace |
| | | the Not Found View by using the |
| | | If your application uses :term:`imperative configuration`, you can replace the |
| | | Not Found View by using the |
| | | :meth:`pyramid.config.Configurator.add_notfound_view` method: |
| | | |
| | | .. code-block:: python |
| | |
| | | config = Configurator() |
| | | config.scan() |
| | | |
| | | The ``notfound_get`` view will be called when a view could not be found and |
| | | the request method was ``GET``. The ``notfound_post`` view will be called |
| | | when a view could not be found and the request method was ``POST``. |
| | | The ``notfound_get`` view will be called when a view could not be found and the |
| | | request method was ``GET``. The ``notfound_post`` view will be called when a |
| | | view could not be found and the request method was ``POST``. |
| | | |
| | | Like any other view, the Not Found View must accept at least a ``request`` |
| | | parameter, or both ``context`` and ``request``. The ``request`` is the |
| | | current :term:`request` representing the denied action. The ``context`` (if |
| | | used in the call signature) will be the instance of the |
| | | parameter, or both ``context`` and ``request``. The ``request`` is the current |
| | | :term:`request` representing the denied action. The ``context`` (if used in |
| | | the call signature) will be the instance of the |
| | | :exc:`~pyramid.httpexceptions.HTTPNotFound` exception that caused the view to |
| | | be called. |
| | | |
| | |
| | | |
| | | .. note:: |
| | | |
| | | When a Not Found View callable is invoked, it is passed a |
| | | :term:`request`. The ``exception`` attribute of the request will be an |
| | | instance of the :exc:`~pyramid.httpexceptions.HTTPNotFound` exception that |
| | | caused the Not Found View to be called. The value of |
| | | ``request.exception.message`` will be a value explaining why the Not Found |
| | | error was raised. This message has different values depending whether the |
| | | ``pyramid.debug_notfound`` environment setting is true or false. |
| | | When a Not Found View callable is invoked, it is passed a :term:`request`. |
| | | The ``exception`` attribute of the request will be an instance of the |
| | | :exc:`~pyramid.httpexceptions.HTTPNotFound` exception that caused the Not |
| | | Found View to be called. The value of ``request.exception.message`` will be |
| | | a value explaining why the Not Found exception was raised. This message has |
| | | different values depending on whether the ``pyramid.debug_notfound`` |
| | | environment setting is true or false. |
| | | |
| | | .. note:: |
| | | |
| | |
| | | |
| | | .. warning:: |
| | | |
| | | When a Not Found View callable accepts an argument list as |
| | | described in :ref:`request_and_context_view_definitions`, the ``context`` |
| | | passed as the first argument to the view callable will be the |
| | | When a Not Found View callable accepts an argument list as described in |
| | | :ref:`request_and_context_view_definitions`, the ``context`` passed as the |
| | | first argument to the view callable will be the |
| | | :exc:`~pyramid.httpexceptions.HTTPNotFound` exception instance. If |
| | | available, the resource context will still be available as |
| | | ``request.context``. |
| | |
| | | --------------------------- |
| | | |
| | | When :app:`Pyramid` can't authorize execution of a view based on the |
| | | :term:`authorization policy` in use, it invokes a :term:`forbidden view`. |
| | | The default forbidden response has a 403 status code and is very plain, but |
| | | the view which generates it can be overridden as necessary. |
| | | :term:`authorization policy` in use, it invokes a :term:`forbidden view`. The |
| | | default forbidden response has a 403 status code and is very plain, but the |
| | | view which generates it can be overridden as necessary. |
| | | |
| | | The :term:`forbidden view` callable is a view callable like any other. The |
| | | :term:`view configuration` which causes it to be a "forbidden" view consists |
| | | of using the :meth:`pyramid.config.Configurator.add_forbidden_view` API or the |
| | | :term:`view configuration` which causes it to be a "forbidden" view consists of |
| | | using the :meth:`pyramid.config.Configurator.add_forbidden_view` API or the |
| | | :class:`pyramid.view.forbidden_view_config` decorator. |
| | | |
| | | For example, you can add a forbidden view by using the |
| | |
| | | config.scan() |
| | | |
| | | Like any other view, the forbidden view must accept at least a ``request`` |
| | | parameter, or both ``context`` and ``request``. If a forbidden view |
| | | callable accepts both ``context`` and ``request``, the HTTP Exception is passed |
| | | as context. The ``context`` as found by the router when view was |
| | | denied (that you normally would expect) is available as |
| | | ``request.context``. The ``request`` is the current :term:`request` |
| | | representing the denied action. |
| | | |
| | | |
| | | parameter, or both ``context`` and ``request``. If a forbidden view callable |
| | | accepts both ``context`` and ``request``, the HTTP Exception is passed as |
| | | context. The ``context`` as found by the router when the view was denied (which |
| | | you normally would expect) is available as ``request.context``. The |
| | | ``request`` is the current :term:`request` representing the denied action. |
| | | |
| | | Here's some sample code that implements a minimal forbidden view: |
| | | |
| | |
| | | |
| | | .. note:: |
| | | |
| | | When a forbidden view callable is invoked, it is passed a |
| | | :term:`request`. The ``exception`` attribute of the request will be an |
| | | instance of the :exc:`~pyramid.httpexceptions.HTTPForbidden` exception |
| | | that caused the forbidden view to be called. The value of |
| | | ``request.exception.message`` will be a value explaining why the forbidden |
| | | was raised and ``request.exception.result`` will be extended information |
| | | about the forbidden exception. These messages have different values |
| | | depending whether the ``pyramid.debug_authorization`` environment setting |
| | | is true or false. |
| | | When a forbidden view callable is invoked, it is passed a :term:`request`. |
| | | The ``exception`` attribute of the request will be an instance of the |
| | | :exc:`~pyramid.httpexceptions.HTTPForbidden` exception that caused the |
| | | forbidden view to be called. The value of ``request.exception.message`` |
| | | will be a value explaining why the forbidden exception was raised, and |
| | | ``request.exception.result`` will be extended information about the |
| | | forbidden exception. These messages have different values depending on |
| | | whether the ``pyramid.debug_authorization`` environment setting is true or |
| | | false. |
| | | |
| | | .. index:: |
| | | single: request factory |
| | |
| | | |
| | | Whenever :app:`Pyramid` handles a request from a :term:`WSGI` server, it |
| | | creates a :term:`request` object based on the WSGI environment it has been |
| | | passed. By default, an instance of the :class:`pyramid.request.Request` |
| | | class is created to represent the request object. |
| | | passed. By default, an instance of the :class:`pyramid.request.Request` class |
| | | is created to represent the request object. |
| | | |
| | | The class (aka "factory") that :app:`Pyramid` uses to create a request object |
| | | instance can be changed by passing a ``request_factory`` argument to the |
| | | The class (a.k.a., "factory") that :app:`Pyramid` uses to create a request |
| | | object instance can be changed by passing a ``request_factory`` argument to the |
| | | constructor of the :term:`configurator`. This argument can be either a |
| | | callable or a :term:`dotted Python name` representing a callable. |
| | | |
| | |
| | | config = Configurator(request_factory=MyRequest) |
| | | |
| | | If you're doing imperative configuration, and you'd rather do it after you've |
| | | already constructed a :term:`configurator` it can also be registered via the |
| | | already constructed a :term:`configurator`, it can also be registered via the |
| | | :meth:`pyramid.config.Configurator.set_request_factory` method: |
| | | |
| | | .. code-block:: python |
| | |
| | | |
| | | .. _adding_request_method: |
| | | |
| | | Adding Methods or Properties to Request Object |
| | | ---------------------------------------------- |
| | | Adding Methods or Properties to a Request Object |
| | | ------------------------------------------------ |
| | | |
| | | .. versionadded:: 1.4. |
| | | |
| | | Since each Pyramid application can only have one :term:`request` factory, |
| | | :ref:`changing the request factory <changing_the_request_factory>` |
| | | is not that extensible, especially if you want to build composable features |
| | | (e.g., Pyramid add-ons and plugins). |
| | | :ref:`changing the request factory <changing_the_request_factory>` is not that |
| | | extensible, especially if you want to build composable features (e.g., Pyramid |
| | | add-ons and plugins). |
| | | |
| | | A lazy property can be registered to the request object via the |
| | | :meth:`pyramid.config.Configurator.add_request_method` API. This allows you |
| | | to specify a callable that will be available on the request object, but will not |
| | | :meth:`pyramid.config.Configurator.add_request_method` API. This allows you to |
| | | specify a callable that will be available on the request object, but will not |
| | | actually execute the function until accessed. |
| | | |
| | | .. warning:: |
| | |
| | | config.add_request_method(total) |
| | | config.add_request_method(prop, reify=True) |
| | | |
| | | In the above example, ``total`` is added as a method. However, ``prop`` is added |
| | | as a property and its result is cached per-request by setting ``reify=True``. |
| | | This way, we eliminate the overhead of running the function multiple times. |
| | | In the above example, ``total`` is added as a method. However, ``prop`` is |
| | | added as a property and its result is cached per-request by setting |
| | | ``reify=True``. This way, we eliminate the overhead of running the function |
| | | multiple times. |
| | | |
| | | >>> request.total(1, 2, 3) |
| | | 6 |
| | |
| | | .. _changing_the_response_factory: |
| | | |
| | | Changing the Response Factory |
| | | ------------------------------- |
| | | ----------------------------- |
| | | |
| | | .. versionadded:: 1.6 |
| | | |
| | | Whenever :app:`Pyramid` returns a response from a view it creates a |
| | | Whenever :app:`Pyramid` returns a response from a view, it creates a |
| | | :term:`response` object. By default, an instance of the |
| | | :class:`pyramid.response.Response` class is created to represent the response |
| | | object. |
| | | |
| | | The factory that :app:`Pyramid` uses to create a response object instance can be |
| | | changed by passing a :class:`pyramid.interfaces.IResponseFactory` argument to |
| | | the constructor of the :term:`configurator`. This argument can be either a |
| | | The factory that :app:`Pyramid` uses to create a response object instance can |
| | | be changed by passing a :class:`pyramid.interfaces.IResponseFactory` argument |
| | | to the constructor of the :term:`configurator`. This argument can be either a |
| | | callable or a :term:`dotted Python name` representing a callable. |
| | | |
| | | The factory takes a single positional argument, which is a :term:`Request` |
| | |
| | | |
| | | config = Configurator(response_factory=lambda r: MyResponse()) |
| | | |
| | | If you're doing imperative configuration, and you'd rather do it after you've |
| | | already constructed a :term:`configurator` it can also be registered via the |
| | | If you're doing imperative configuration and you'd rather do it after you've |
| | | already constructed a :term:`configurator`, it can also be registered via the |
| | | :meth:`pyramid.config.Configurator.set_response_factory` method: |
| | | |
| | | .. code-block:: python |
| | |
| | | |
| | | .. _beforerender_event: |
| | | |
| | | Using The Before Render Event |
| | | Using the Before Render Event |
| | | ----------------------------- |
| | | |
| | | Subscribers to the :class:`pyramid.events.BeforeRender` event may introspect |
| | | and modify the set of :term:`renderer globals` before they are passed to a |
| | | :term:`renderer`. This event object iself has a dictionary-like interface |
| | | that can be used for this purpose. For example: |
| | | :term:`renderer`. This event object iself has a dictionary-like interface that |
| | | can be used for this purpose. For example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | def add_global(event): |
| | | event['mykey'] = 'foo' |
| | | |
| | | An object of this type is sent as an event just before a :term:`renderer` |
| | | is invoked. |
| | | An object of this type is sent as an event just before a :term:`renderer` is |
| | | invoked. |
| | | |
| | | If a subscriber attempts to add a key that already exist in the renderer |
| | | If a subscriber attempts to add a key that already exists in the renderer |
| | | globals dictionary, a :exc:`KeyError` is raised. This limitation is enforced |
| | | because event subscribers do not possess any relative ordering. The set of |
| | | keys added to the renderer globals dictionary by all |
| | | :class:`pyramid.events.BeforeRender` subscribers and renderer globals |
| | | factories must be unique. |
| | | :class:`pyramid.events.BeforeRender` subscribers and renderer globals factories |
| | | must be unique. |
| | | |
| | | The dictionary returned from the view is accessible through the |
| | | :attr:`rendering_val` attribute of a :class:`~pyramid.events.BeforeRender` |
| | | event. |
| | | |
| | | Suppose you return ``{'mykey': 'somevalue', 'mykey2': 'somevalue2'}`` from |
| | | your view callable, like so: |
| | | Suppose you return ``{'mykey': 'somevalue', 'mykey2': 'somevalue2'}`` from your |
| | | view callable, like so: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | response.cache_control.max_age = 360 |
| | | request.add_response_callback(cache_callback) |
| | | |
| | | No response callback is called if an unhandled exception happens in |
| | | application code, or if the response object returned by a :term:`view |
| | | callable` is invalid. Response callbacks *are*, however, invoked when a |
| | | :term:`exception view` is rendered successfully: in such a case, the |
| | | :attr:`request.exception` attribute of the request when it enters a response |
| | | callback will be an exception object instead of its default value of |
| | | ``None``. |
| | | No response callback is called if an unhandled exception happens in application |
| | | code, or if the response object returned by a :term:`view callable` is invalid. |
| | | Response callbacks *are*, however, invoked when a :term:`exception view` is |
| | | rendered successfully. In such a case, the :attr:`request.exception` attribute |
| | | of the request when it enters a response callback will be an exception object |
| | | instead of its default value of ``None``. |
| | | |
| | | Response callbacks are called in the order they're added |
| | | (first-to-most-recently-added). All response callbacks are called *before* |
| | | the :class:`~pyramid.events.NewResponse` event is sent. Errors raised by |
| | | response callbacks are not handled specially. They will be propagated to the |
| | | caller of the :app:`Pyramid` router application. |
| | | (first-to-most-recently-added). All response callbacks are called *before* the |
| | | :class:`~pyramid.events.NewResponse` event is sent. Errors raised by response |
| | | callbacks are not handled specially. They will be propagated to the caller of |
| | | the :app:`Pyramid` router application. |
| | | |
| | | A response callback has a lifetime of a *single* request. If you want a |
| | | response callback to happen as the result of *every* request, you must |
| | | re-register the callback into every new request (perhaps within a subscriber |
| | | of a :class:`~pyramid.events.NewRequest` event). |
| | | re-register the callback into every new request (perhaps within a subscriber of |
| | | a :class:`~pyramid.events.NewRequest` event). |
| | | |
| | | .. index:: |
| | | single: finished callback |
| | |
| | | ------------------------ |
| | | |
| | | A :term:`finished callback` is a function that will be called unconditionally |
| | | by the :app:`Pyramid` :term:`router` at the very end of request processing. |
| | | A finished callback can be used to perform an action at the end of a request |
| | | by the :app:`Pyramid` :term:`router` at the very end of request processing. A |
| | | finished callback can be used to perform an action at the end of a request |
| | | unconditionally. |
| | | |
| | | The :meth:`pyramid.request.Request.add_finished_callback` method is used to |
| | | register a finished callback. |
| | | |
| | | A finished callback is a callable which accepts a single positional |
| | | parameter: ``request``. For example: |
| | | A finished callback is a callable which accepts a single positional parameter: |
| | | ``request``. For example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | request.add_finished_callback(log_callback) |
| | | |
| | | Finished callbacks are called in the order they're added |
| | | (first-to-most-recently-added). Finished callbacks (unlike a |
| | | :term:`response callback`) are *always* called, even if an exception |
| | | happens in application code that prevents a response from being |
| | | generated. |
| | | (first-to-most-recently-added). Finished callbacks (unlike a :term:`response |
| | | callback`) are *always* called, even if an exception happens in application |
| | | code that prevents a response from being generated. |
| | | |
| | | The set of finished callbacks associated with a request are called *very |
| | | late* in the processing of that request; they are essentially the very last |
| | | thing called by the :term:`router` before a request "ends". They are called |
| | | after response processing has already occurred in a top-level ``finally:`` |
| | | block within the router request processing code. As a result, mutations |
| | | performed to the ``request`` provided to a finished callback will have no |
| | | meaningful effect, because response processing will have already occurred, |
| | | and the request's scope will expire almost immediately after all finished |
| | | callbacks have been processed. |
| | | The set of finished callbacks associated with a request are called *very late* |
| | | in the processing of that request; they are essentially the very last thing |
| | | called by the :term:`router` before a request "ends". They are called after |
| | | response processing has already occurred in a top-level ``finally:`` block |
| | | within the router request processing code. As a result, mutations performed to |
| | | the ``request`` provided to a finished callback will have no meaningful effect, |
| | | because response processing will have already occurred, and the request's scope |
| | | will expire almost immediately after all finished callbacks have been |
| | | processed. |
| | | |
| | | Errors raised by finished callbacks are not handled specially. They |
| | | will be propagated to the caller of the :app:`Pyramid` router |
| | | application. |
| | | Errors raised by finished callbacks are not handled specially. They will be |
| | | propagated to the caller of the :app:`Pyramid` router application. |
| | | |
| | | A finished callback has a lifetime of a *single* request. If you want a |
| | | finished callback to happen as the result of *every* request, you must |
| | | re-register the callback into every new request (perhaps within a subscriber |
| | | of a :class:`~pyramid.events.NewRequest` event). |
| | | re-register the callback into every new request (perhaps within a subscriber of |
| | | a :class:`~pyramid.events.NewRequest` event). |
| | | |
| | | .. index:: |
| | | single: traverser |
| | |
| | | |
| | | The default :term:`traversal` algorithm that :app:`Pyramid` uses is explained |
| | | in :ref:`traversal_algorithm`. Though it is rarely necessary, this default |
| | | algorithm can be swapped out selectively for a different traversal pattern |
| | | via configuration. |
| | | algorithm can be swapped out selectively for a different traversal pattern via |
| | | configuration. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | More than one traversal algorithm can be active at the same time. For |
| | | instance, if your :term:`root factory` returns more than one type of object |
| | | conditionally, you could claim that an alternate traverser adapter is "for" |
| | | conditionally, you could claim that an alternative traverser adapter is "for" |
| | | only one particular class or interface. When the root factory returned an |
| | | object that implemented that class or interface, a custom traverser would be |
| | | used. Otherwise, the default traverser would be used. For example: |
| | | used. Otherwise the default traverser would be used. For example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | config.add_traverser(Traverser, MyRoot) |
| | | |
| | | If the above stanza was added to a Pyramid ``__init__.py`` file's ``main`` |
| | | function, :app:`Pyramid` would use the ``myapp.traversal.Traverser`` only |
| | | when the application :term:`root factory` returned an instance of the |
| | | function, :app:`Pyramid` would use the ``myapp.traversal.Traverser`` only when |
| | | the application :term:`root factory` returned an instance of the |
| | | ``myapp.resources.MyRoot`` object. Otherwise it would use the default |
| | | :app:`Pyramid` traverser to do traversal. |
| | | |
| | | .. index:: |
| | | single: url generator |
| | | single: URL generator |
| | | |
| | | .. _changing_resource_url: |
| | | |
| | |
| | | When you add a traverser as described in :ref:`changing_the_traverser`, it's |
| | | often convenient to continue to use the |
| | | :meth:`pyramid.request.Request.resource_url` API. However, since the way |
| | | traversal is done will have been modified, the URLs it generates by default |
| | | may be incorrect when used against resources derived from your custom |
| | | traverser. |
| | | traversal is done will have been modified, the URLs it generates by default may |
| | | be incorrect when used against resources derived from your custom traverser. |
| | | |
| | | If you've added a traverser, you can change how |
| | | :meth:`~pyramid.request.Request.resource_url` generates a URL for a specific |
| | |
| | | |
| | | config.add_resource_url_adapter(ResourceURLAdapter, MyRoot) |
| | | |
| | | In the above example, the ``myapp.traversal.ResourceURLAdapter`` class will |
| | | be used to provide services to :meth:`~pyramid.request.Request.resource_url` |
| | | any time the :term:`resource` passed to ``resource_url`` is of the class |
| | | In the above example, the ``myapp.traversal.ResourceURLAdapter`` class will be |
| | | used to provide services to :meth:`~pyramid.request.Request.resource_url` any |
| | | time the :term:`resource` passed to ``resource_url`` is of the class |
| | | ``myapp.resources.MyRoot``. The ``resource_iface`` argument ``MyRoot`` |
| | | represents the type of interface that must be possessed by the resource for |
| | | this resource url factory to be found. If the ``resource_iface`` argument is |
| | | omitted, this resource url adapter will be used for *all* resources. |
| | | omitted, this resource URL adapter will be used for *all* resources. |
| | | |
| | | The API that must be implemented by a class that provides |
| | | :class:`~pyramid.interfaces.IResourceURL` is as follows: |
| | |
| | | resource |
| | | """ |
| | | def __init__(self, resource, request): |
| | | """ Accept the resource and request and set self.physical_path and |
| | | self.virtual_path""" |
| | | """ Accept the resource and request and set self.physical_path and |
| | | self.virtual_path """ |
| | | self.virtual_path = some_function_of(resource, request) |
| | | self.physical_path = some_other_function_of(resource, request) |
| | | |
| | |
| | | <https://github.com/Pylons/pyramid/blob/master/pyramid/traversal.py>`_ of the |
| | | :term:`Pylons` GitHub Pyramid repository. |
| | | |
| | | See :meth:`pyramid.config.add_resource_url_adapter` for more information. |
| | | See :meth:`pyramid.config.Configurator.add_resource_url_adapter` for more |
| | | information. |
| | | |
| | | .. index:: |
| | | single: IResponse |
| | |
| | | :meth:`pyramid.config.Configurator.add_response_adapter` or the |
| | | :class:`~pyramid.response.response_adapter` decorator. |
| | | |
| | | Pyramid, in various places, adapts the result of calling a view callable to |
| | | the :class:`~pyramid.interfaces.IResponse` interface to ensure that the |
| | | object returned by the view callable is a "true" response object. The vast |
| | | majority of time, the result of this adaptation is the result object itself, |
| | | as view callables written by "civilians" who read the narrative documentation |
| | | contained in this manual will always return something that implements the |
| | | :class:`~pyramid.interfaces.IResponse` interface. Most typically, this will |
| | | be an instance of the :class:`pyramid.response.Response` class or a subclass. |
| | | If a civilian returns a non-Response object from a view callable that isn't |
| | | configured to use a :term:`renderer`, he will typically expect the router to |
| | | Pyramid, in various places, adapts the result of calling a view callable to the |
| | | :class:`~pyramid.interfaces.IResponse` interface to ensure that the object |
| | | returned by the view callable is a "true" response object. The vast majority |
| | | of time, the result of this adaptation is the result object itself, as view |
| | | callables written by "civilians" who read the narrative documentation contained |
| | | in this manual will always return something that implements the |
| | | :class:`~pyramid.interfaces.IResponse` interface. Most typically, this will be |
| | | an instance of the :class:`pyramid.response.Response` class or a subclass. If a |
| | | civilian returns a non-Response object from a view callable that isn't |
| | | configured to use a :term:`renderer`, they will typically expect the router to |
| | | raise an error. However, you can hook Pyramid in such a way that users can |
| | | return arbitrary values from a view callable by providing an adapter which |
| | | converts the arbitrary return value into something that implements |
| | | :class:`~pyramid.interfaces.IResponse`. |
| | | |
| | | For example, if you'd like to allow view callables to return bare string |
| | | objects (without requiring a :term:`renderer` to convert a string to a |
| | | response object), you can register an adapter which converts the string to a |
| | | Response: |
| | | objects (without requiring a :term:`renderer` to convert a string to a response |
| | | object), you can register an adapter which converts the string to a Response: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | config.add_response_adapter(string_response_adapter, str) |
| | | |
| | | Likewise, if you want to be able to return a simplified kind of response |
| | | object from view callables, you can use the IResponse hook to register an |
| | | adapter to the more complex IResponse interface: |
| | | Likewise, if you want to be able to return a simplified kind of response object |
| | | from view callables, you can use the IResponse hook to register an adapter to |
| | | the more complex IResponse interface: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | If you want to implement your own Response object instead of using the |
| | | :class:`pyramid.response.Response` object in any capacity at all, you'll have |
| | | to make sure the object implements every attribute and method outlined in |
| | | to make sure that the object implements every attribute and method outlined in |
| | | :class:`pyramid.interfaces.IResponse` and you'll have to ensure that it uses |
| | | ``zope.interface.implementer(IResponse)`` as a class decorator. |
| | | |
| | |
| | | |
| | | @implementer(IResponse) |
| | | class MyResponse(object): |
| | | # ... an implementation of every method and attribute |
| | | # ... an implementation of every method and attribute |
| | | # documented in IResponse should follow ... |
| | | |
| | | When an alternate response object implementation is returned by a view |
| | |
| | | subclasses of the class) will natively provide IResponse. The adapter |
| | | registered for ``webob.Response`` simply returns the response object. |
| | | |
| | | Instead of using :meth:`pyramid.config.Configurator.add_response_adapter`, |
| | | you can use the :class:`pyramid.response.response_adapter` decorator: |
| | | Instead of using :meth:`pyramid.config.Configurator.add_response_adapter`, you |
| | | can use the :class:`pyramid.response.response_adapter` decorator: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | A view mapper is an object that accepts a set of keyword arguments and which |
| | | returns a callable. The returned callable is called with the :term:`view |
| | | callable` object. The returned callable should itself return another |
| | | callable which can be called with the "internal calling protocol" ``(context, |
| | | callable` object. The returned callable should itself return another callable |
| | | which can be called with the "internal calling protocol" ``(context, |
| | | request)``. |
| | | |
| | | You can use a view mapper in a number of ways: |
| | | |
| | | - by setting a ``__view_mapper__`` attribute (which is the view mapper |
| | | object) on the view callable itself |
| | | - by setting a ``__view_mapper__`` attribute (which is the view mapper object) |
| | | on the view callable itself |
| | | |
| | | - by passing the mapper object to |
| | | :meth:`pyramid.config.Configurator.add_view` (or its declarative/decorator |
| | | equivalents) as the ``mapper`` argument. |
| | | - by passing the mapper object to :meth:`pyramid.config.Configurator.add_view` |
| | | (or its declarative and decorator equivalents) as the ``mapper`` argument |
| | | |
| | | - by registering a *default* view mapper. |
| | | - by registering a *default* view mapper |
| | | |
| | | Here's an example of a view mapper that emulates (somewhat) a Pylons |
| | | "controller". The mapper is initialized with some keyword arguments. Its |
| | | ``__call__`` method accepts the view object (which will be a class). It uses |
| | | the ``attr`` keyword argument it is passed to determine which attribute |
| | | should be used as an action method. The wrapper method it returns accepts |
| | | ``(context, request)`` and returns the result of calling the action method |
| | | with keyword arguments implied by the :term:`matchdict` after popping the |
| | | ``action`` out of it. This somewhat emulates the Pylons style of calling |
| | | action methods with routing parameters pulled out of the route matching dict |
| | | as keyword arguments. |
| | | the ``attr`` keyword argument it is passed to determine which attribute should |
| | | be used as an action method. The wrapper method it returns accepts ``(context, |
| | | request)`` and returns the result of calling the action method with keyword |
| | | arguments implied by the :term:`matchdict` after popping the ``action`` out of |
| | | it. This somewhat emulates the Pylons style of calling action methods with |
| | | routing parameters pulled out of the route matching dict as keyword arguments. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | set a *default* view mapper (overriding the superdefault view mapper used by |
| | | Pyramid itself). |
| | | |
| | | A *single* view registration can use a view mapper by passing the mapper as |
| | | the ``mapper`` argument to :meth:`~pyramid.config.Configurator.add_view`. |
| | | A *single* view registration can use a view mapper by passing the mapper as the |
| | | ``mapper`` argument to :meth:`~pyramid.config.Configurator.add_view`. |
| | | |
| | | .. index:: |
| | | single: configuration decorator |
| | |
| | | Registering Configuration Decorators |
| | | ------------------------------------ |
| | | |
| | | Decorators such as :class:`~pyramid.view.view_config` don't change the |
| | | behavior of the functions or classes they're decorating. Instead, when a |
| | | :term:`scan` is performed, a modified version of the function or class is |
| | | registered with :app:`Pyramid`. |
| | | Decorators such as :class:`~pyramid.view.view_config` don't change the behavior |
| | | of the functions or classes they're decorating. Instead when a :term:`scan` is |
| | | performed, a modified version of the function or class is registered with |
| | | :app:`Pyramid`. |
| | | |
| | | You may wish to have your own decorators that offer such behaviour. This is |
| | | possible by using the :term:`Venusian` package in the same way that it is |
| | | used by :app:`Pyramid`. |
| | | possible by using the :term:`Venusian` package in the same way that it is used |
| | | by :app:`Pyramid`. |
| | | |
| | | By way of example, let's suppose you want to write a decorator that registers |
| | | the function it wraps with a :term:`Zope Component Architecture` "utility" |
| | |
| | | completed. A normal decorator would fail as it would be executed before the |
| | | configuration had even begun. |
| | | |
| | | However, using :term:`Venusian`, the decorator could be written as |
| | | follows: |
| | | However, using :term:`Venusian`, the decorator could be written as follows: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | venusian.attach(wrapped, self.register) |
| | | return wrapped |
| | | |
| | | This decorator could then be used to register functions throughout |
| | | your code: |
| | | This decorator could then be used to register functions throughout your code: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | @registerFunction('/some/path') |
| | | def my_function(): |
| | | do_stuff() |
| | | do_stuff() |
| | | |
| | | However, the utility would only be looked up when a :term:`scan` was |
| | | performed, enabling you to set up the utility in advance: |
| | | However, the utility would only be looked up when a :term:`scan` was performed, |
| | | enabling you to set up the utility in advance: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | class UtilityImplementation: |
| | | |
| | | def __init__(self): |
| | | self.registrations = {} |
| | | self.registrations = {} |
| | | |
| | | def register(self, path, callable_): |
| | | self.registrations[path] = callable_ |
| | | self.registrations[path] = callable_ |
| | | |
| | | if __name__ == '__main__': |
| | | config = Configurator() |
| | |
| | | A :term:`tween` (a contraction of the word "between") is a bit of code that |
| | | sits between the Pyramid router's main request handling function and the |
| | | upstream WSGI component that uses :app:`Pyramid` as its "app". This is a |
| | | feature that may be used by Pyramid framework extensions, to provide, for |
| | | feature that may be used by Pyramid framework extensions to provide, for |
| | | example, Pyramid-specific view timing support bookkeeping code that examines |
| | | exceptions before they are returned to the upstream WSGI application. Tweens |
| | | behave a bit like :term:`WSGI` :term:`middleware` but they have the benefit of |
| | | behave a bit like :term:`WSGI` :term:`middleware`, but they have the benefit of |
| | | running in a context in which they have access to the Pyramid :term:`request`, |
| | | :term:`response` and :term:`application registry` as well as the Pyramid |
| | | :term:`response`, and :term:`application registry`, as well as the Pyramid |
| | | rendering machinery. |
| | | |
| | | Creating a Tween |
| | | ~~~~~~~~~~~~~~~~ |
| | | |
| | | To create a tween, you must write a "tween factory". A tween factory |
| | | must be a globally importable callable which accepts two arguments: |
| | | ``handler`` and ``registry``. ``handler`` will be either the main |
| | | Pyramid request handling function or another tween. ``registry`` will be the |
| | | Pyramid :term:`application registry` represented by this Configurator. A |
| | | tween factory must return the tween (a callable object) when it is called. |
| | | To create a tween, you must write a "tween factory". A tween factory must be a |
| | | globally importable callable which accepts two arguments: ``handler`` and |
| | | ``registry``. ``handler`` will be either the main Pyramid request handling |
| | | function or another tween. ``registry`` will be the Pyramid :term:`application |
| | | registry` represented by this Configurator. A tween factory must return the |
| | | tween (a callable object) when it is called. |
| | | |
| | | A tween is called with a single argument, ``request``, which is the |
| | | :term:`request` created by Pyramid's router when it receives a WSGI request. |
| | | A tween should return a :term:`response`, usually the one generated by the |
| | | :term:`request` created by Pyramid's router when it receives a WSGI request. A |
| | | tween should return a :term:`response`, usually the one generated by the |
| | | downstream Pyramid application. |
| | | |
| | | You can write the tween factory as a simple closure-returning function: |
| | |
| | | |
| | | return response |
| | | |
| | | You should avoid mutating any state on the tween instance. The tween is |
| | | invoked once per request and any shared mutable state needs to be carefully |
| | | handled to avoid any race conditions. |
| | | You should avoid mutating any state on the tween instance. The tween is invoked |
| | | once per request and any shared mutable state needs to be carefully handled to |
| | | avoid any race conditions. |
| | | |
| | | The closure style performs slightly better and enables you to conditionally |
| | | omit the tween from the request processing pipeline (see the following timing |
| | | tween example), whereas the class style makes it easier to have shared mutable |
| | | state, and it allows subclassing. |
| | | state and allows subclassing. |
| | | |
| | | Here's a complete example of a tween that logs the time spent processing each |
| | | request: |
| | |
| | | |
| | | In the above example, the tween factory defines a ``timing_tween`` tween and |
| | | returns it if ``asbool(registry.settings.get('do_timing'))`` is true. It |
| | | otherwise simply returns the handler it was given. The ``registry.settings`` |
| | | attribute is a handle to the deployment settings provided by the user |
| | | (usually in an ``.ini`` file). In this case, if the user has defined a |
| | | ``do_timing`` setting, and that setting is ``True``, the user has said she |
| | | wants to do timing, so the tween factory returns the timing tween; it |
| | | otherwise just returns the handler it has been provided, preventing any |
| | | timing. |
| | | otherwise simply returns the handler which it was given. The |
| | | ``registry.settings`` attribute is a handle to the deployment settings provided |
| | | by the user (usually in an ``.ini`` file). In this case, if the user has |
| | | defined a ``do_timing`` setting and that setting is ``True``, the user has said |
| | | they want to do timing, so the tween factory returns the timing tween; it |
| | | otherwise just returns the handler it has been provided, preventing any timing. |
| | | |
| | | The example timing tween simply records the start time, calls the downstream |
| | | handler, logs the number of seconds consumed by the downstream handler, and |
| | |
| | | Note that you must use a :term:`dotted Python name` as the first argument to |
| | | :meth:`pyramid.config.Configurator.add_tween`; this must point at a tween |
| | | factory. You cannot pass the tween factory object itself to the method: it |
| | | must be :term:`dotted Python name` that points to a globally importable |
| | | object. In the above example, we assume that a ``timing_tween_factory`` |
| | | tween factory was defined in a module named ``myapp.tweens``, so the tween |
| | | factory is importable as ``myapp.tweens.timing_tween_factory``. |
| | | must be :term:`dotted Python name` that points to a globally importable object. |
| | | In the above example, we assume that a ``timing_tween_factory`` tween factory |
| | | was defined in a module named ``myapp.tweens``, so the tween factory is |
| | | importable as ``myapp.tweens.timing_tween_factory``. |
| | | |
| | | When you use :meth:`pyramid.config.Configurator.add_tween`, you're |
| | | instructing the system to use your tween factory at startup time unless the |
| | | user has provided an explicit tween list in his configuration. This is |
| | | what's meant by an "implicit" tween. A user can always elect to supply an |
| | | explicit tween list, reordering or disincluding implicitly added tweens. See |
| | | When you use :meth:`pyramid.config.Configurator.add_tween`, you're instructing |
| | | the system to use your tween factory at startup time unless the user has |
| | | provided an explicit tween list in their configuration. This is what's meant |
| | | by an "implicit" tween. A user can always elect to supply an explicit tween |
| | | list, reordering or disincluding implicitly added tweens. See |
| | | :ref:`explicit_tween_ordering` for more information about explicit tween |
| | | ordering. |
| | | |
| | | If more than one call to :meth:`pyramid.config.Configurator.add_tween` is |
| | | made within a single application configuration, the tweens will be chained |
| | | together at application startup time. The *first* tween factory added via |
| | | ``add_tween`` will be called with the Pyramid exception view tween factory as |
| | | its ``handler`` argument, then the tween factory added directly after that |
| | | one will be called with the result of the first tween factory as its |
| | | ``handler`` argument, and so on, ad infinitum until all tween factories have |
| | | been called. The Pyramid router will use the outermost tween produced by this |
| | | chain (the tween generated by the very last tween factory added) as its |
| | | request handler function. For example: |
| | | If more than one call to :meth:`pyramid.config.Configurator.add_tween` is made |
| | | within a single application configuration, the tweens will be chained together |
| | | at application startup time. The *first* tween factory added via ``add_tween`` |
| | | will be called with the Pyramid exception view tween factory as its ``handler`` |
| | | argument, then the tween factory added directly after that one will be called |
| | | with the result of the first tween factory as its ``handler`` argument, and so |
| | | on, ad infinitum until all tween factories have been called. The Pyramid router |
| | | will use the outermost tween produced by this chain (the tween generated by the |
| | | very last tween factory added) as its request handler function. For example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | config.add_tween('myapp.tween_factory1') |
| | | config.add_tween('myapp.tween_factory2') |
| | | |
| | | The above example will generate an implicit tween chain that looks like |
| | | this:: |
| | | The above example will generate an implicit tween chain that looks like this:: |
| | | |
| | | INGRESS (implicit) |
| | | myapp.tween_factory2 |
| | |
| | | By default, as described above, the ordering of the chain is controlled |
| | | entirely by the relative ordering of calls to |
| | | :meth:`pyramid.config.Configurator.add_tween`. However, the caller of |
| | | add_tween can provide an optional hint that can influence the implicit tween |
| | | chain ordering by supplying ``under`` or ``over`` (or both) arguments to |
| | | :meth:`~pyramid.config.Configurator.add_tween`. These hints are only |
| | | used when an explicit tween ordering is not used. See |
| | | :ref:`explicit_tween_ordering` for a description of how to set an explicit |
| | | tween ordering. |
| | | ``add_tween`` can provide an optional hint that can influence the implicit |
| | | tween chain ordering by supplying ``under`` or ``over`` (or both) arguments to |
| | | :meth:`~pyramid.config.Configurator.add_tween`. These hints are only used when |
| | | an explicit tween ordering is not used. See :ref:`explicit_tween_ordering` for |
| | | a description of how to set an explicit tween ordering. |
| | | |
| | | Allowable values for ``under`` or ``over`` (or both) are: |
| | | |
| | | - ``None`` (the default). |
| | | - ``None`` (the default), |
| | | |
| | | - A :term:`dotted Python name` to a tween factory: a string representing the |
| | | predicted dotted name of a tween factory added in a call to ``add_tween`` |
| | | in the same configuration session. |
| | | - a :term:`dotted Python name` to a tween factory: a string representing the |
| | | predicted dotted name of a tween factory added in a call to ``add_tween`` in |
| | | the same configuration session, |
| | | |
| | | - One of the constants :attr:`pyramid.tweens.MAIN`, |
| | | :attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`. |
| | | - one of the constants :attr:`pyramid.tweens.MAIN`, |
| | | :attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`, or |
| | | |
| | | - An iterable of any combination of the above. This allows the user to specify |
| | | - an iterable of any combination of the above. This allows the user to specify |
| | | fallbacks if the desired tween is not included, as well as compatibility |
| | | with multiple other tweens. |
| | | |
| | | Effectively, ``over`` means "closer to the request ingress than" and |
| | | ``under`` means "closer to the main Pyramid application than". |
| | | You can think of an onion with outer layers over the inner layers, |
| | | the application being under all the layers at the center. |
| | | Effectively, ``over`` means "closer to the request ingress than" and ``under`` |
| | | means "closer to the main Pyramid application than". You can think of an onion |
| | | with outer layers over the inner layers, the application being under all the |
| | | layers at the center. |
| | | |
| | | For example, the following call to |
| | | :meth:`~pyramid.config.Configurator.add_tween` will attempt to place the |
| | | tween factory represented by ``myapp.tween_factory`` directly 'above' (in |
| | | ``ptweens`` order) the main Pyramid request handler. |
| | | :meth:`~pyramid.config.Configurator.add_tween` will attempt to place the tween |
| | | factory represented by ``myapp.tween_factory`` directly "above" (in ``ptweens`` |
| | | order) the main Pyramid request handler. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | config.add_tween('myapp.tween_factory', over=pyramid.tweens.MAIN) |
| | | |
| | | The above example will generate an implicit tween chain that looks like |
| | | this:: |
| | | The above example will generate an implicit tween chain that looks like this:: |
| | | |
| | | INGRESS (implicit) |
| | | pyramid.tweens.excview_tween_factory (implicit) |
| | |
| | | MAIN (implicit) |
| | | |
| | | Likewise, calling the following call to |
| | | :meth:`~pyramid.config.Configurator.add_tween` will attempt to place this |
| | | tween factory 'above' the main handler but 'below' a separately added tween |
| | | factory: |
| | | :meth:`~pyramid.config.Configurator.add_tween` will attempt to place this tween |
| | | factory "above" the main handler but "below" a separately added tween factory: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | over=pyramid.tweens.MAIN, |
| | | under='myapp.tween_factory1') |
| | | |
| | | The above example will generate an implicit tween chain that looks like |
| | | this:: |
| | | The above example will generate an implicit tween chain that looks like this:: |
| | | |
| | | INGRESS (implicit) |
| | | pyramid.tweens.excview_tween_factory (implicit) |
| | |
| | | |
| | | If all options for ``under`` (or ``over``) cannot be found in the current |
| | | configuration, it is an error. If some options are specified purely for |
| | | compatibilty with other tweens, just add a fallback of MAIN or INGRESS. |
| | | For example, ``under=('someothertween', 'someothertween2', INGRESS)``. |
| | | This constraint will require the tween to be located under both the |
| | | 'someothertween' tween, the 'someothertween2' tween, and INGRESS. If any of |
| | | these is not in the current configuration, this constraint will only organize |
| | | itself based on the tweens that are present. |
| | | compatibilty with other tweens, just add a fallback of ``MAIN`` or ``INGRESS``. |
| | | For example, ``under=('someothertween', 'someothertween2', INGRESS)``. This |
| | | constraint will require the tween to be located under the ``someothertween`` |
| | | tween, the ``someothertween2`` tween, and ``INGRESS``. If any of these is not |
| | | in the current configuration, this constraint will only organize itself based |
| | | on the tweens that are present. |
| | | |
| | | .. _explicit_tween_ordering: |
| | | |
| | | Explicit Tween Ordering |
| | | ~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Implicit tween ordering is obviously only best-effort. Pyramid will attempt |
| | | to provide an implicit order of tweens as best it can using hints provided by |
| | | calls to :meth:`~pyramid.config.Configurator.add_tween`, but because it's |
| | | only best-effort, if very precise tween ordering is required, the only |
| | | surefire way to get it is to use an explicit tween order. The deploying user |
| | | can override the implicit tween inclusion and ordering implied by calls to |
| | | Implicit tween ordering is obviously only best-effort. Pyramid will attempt to |
| | | provide an implicit order of tweens as best it can using hints provided by |
| | | calls to :meth:`~pyramid.config.Configurator.add_tween`. But because it's only |
| | | best-effort, if very precise tween ordering is required, the only surefire way |
| | | to get it is to use an explicit tween order. The deploying user can override |
| | | the implicit tween inclusion and ordering implied by calls to |
| | | :meth:`~pyramid.config.Configurator.add_tween` entirely by using the |
| | | ``pyramid.tweens`` settings value. When used, this settings value must be a |
| | | list of Python dotted names which will override the ordering (and inclusion) |
| | | of tween factories in the implicit tween chain. For example: |
| | | list of Python dotted names which will override the ordering (and inclusion) of |
| | | tween factories in the implicit tween chain. For example: |
| | | |
| | | .. code-block:: ini |
| | | :linenos: |
| | |
| | | In the above configuration, calls made during configuration to |
| | | :meth:`pyramid.config.Configurator.add_tween` are ignored, and the user is |
| | | telling the system to use the tween factories he has listed in the |
| | | ``pyramid.tweens`` configuration setting (each is a :term:`dotted Python |
| | | name` which points to a tween factory) instead of any tween factories added |
| | | via :meth:`pyramid.config.Configurator.add_tween`. The *first* tween factory |
| | | in the ``pyramid.tweens`` list will be used as the producer of the effective |
| | | ``pyramid.tweens`` configuration setting (each is a :term:`dotted Python name` |
| | | which points to a tween factory) instead of any tween factories added via |
| | | :meth:`pyramid.config.Configurator.add_tween`. The *first* tween factory in |
| | | the ``pyramid.tweens`` list will be used as the producer of the effective |
| | | :app:`Pyramid` request handling function; it will wrap the tween factory |
| | | declared directly "below" it, ad infinitum. The "main" Pyramid request |
| | | handler is implicit, and always "at the bottom". |
| | | declared directly "below" it, ad infinitum. The "main" Pyramid request handler |
| | | is implicit, and always "at the bottom". |
| | | |
| | | .. note:: |
| | | |
| | | Pyramid's own :term:`exception view` handling logic is implemented |
| | | as a tween factory function: :func:`pyramid.tweens.excview_tween_factory`. |
| | | If Pyramid exception view handling is desired, and tween factories are |
| | | Pyramid's own :term:`exception view` handling logic is implemented as a |
| | | tween factory function: :func:`pyramid.tweens.excview_tween_factory`. If |
| | | Pyramid exception view handling is desired, and tween factories are |
| | | specified via the ``pyramid.tweens`` configuration setting, the |
| | | :func:`pyramid.tweens.excview_tween_factory` function must be added to the |
| | | ``pyramid.tweens`` configuration setting list explicitly. If it is not |
| | |
| | | Tween Conflicts and Ordering Cycles |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Pyramid will prevent the same tween factory from being added to the tween |
| | | chain more than once using configuration conflict detection. If you wish to |
| | | add the same tween factory more than once in a configuration, you should |
| | | either: a) use a tween factory that is a separate globally importable |
| | | instance object from the factory that it conflicts with b) use a function or |
| | | class as a tween factory with the same logic as the other tween factory it |
| | | conflicts with but with a different ``__name__`` attribute or c) call |
| | | Pyramid will prevent the same tween factory from being added to the tween chain |
| | | more than once using configuration conflict detection. If you wish to add the |
| | | same tween factory more than once in a configuration, you should either: (a) |
| | | use a tween factory that is a separate globally importable instance object from |
| | | the factory that it conflicts with; (b) use a function or class as a tween |
| | | factory with the same logic as the other tween factory it conflicts with, but |
| | | with a different ``__name__`` attribute; or (c) call |
| | | :meth:`pyramid.config.Configurator.commit` between calls to |
| | | :meth:`pyramid.config.Configurator.add_tween`. |
| | | |
| | | If a cycle is detected in implicit tween ordering when ``over`` and ``under`` |
| | | are used in any call to "add_tween", an exception will be raised at startup |
| | | are used in any call to ``add_tween``, an exception will be raised at startup |
| | | time. |
| | | |
| | | Displaying Tween Ordering |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | The ``ptweens`` command-line utility can be used to report the current |
| | | implict and explicit tween chains used by an application. See |
| | | The ``ptweens`` command-line utility can be used to report the current implict |
| | | and explicit tween chains used by an application. See |
| | | :ref:`displaying_tweens`. |
| | | |
| | | .. _registering_thirdparty_predicates: |
| | | |
| | | Adding A Third Party View, Route, or Subscriber Predicate |
| | | Adding a Third Party View, Route, or Subscriber Predicate |
| | | --------------------------------------------------------- |
| | | |
| | | .. versionadded:: 1.4 |
| | |
| | | View and Route Predicates |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | View and route predicates used during configuration allow you to narrow the |
| | | set of circumstances under which a view or route will match. For example, |
| | | the ``request_method`` view predicate can be used to ensure a view callable |
| | | is only invoked when the request's method is ``POST``: |
| | | View and route predicates used during configuration allow you to narrow the set |
| | | of circumstances under which a view or route will match. For example, the |
| | | ``request_method`` view predicate can be used to ensure a view callable is only |
| | | invoked when the request's method is ``POST``: |
| | | |
| | | .. code-block:: python |
| | | |
| | |
| | | |
| | | config.add_route('name', '/foo', request_method='POST') |
| | | |
| | | Many other built-in predicates exists (``request_param``, and others). You |
| | | can add third-party predicates to the list of available predicates by using |
| | | one of :meth:`pyramid.config.Configurator.add_view_predicate` or |
| | | Many other built-in predicates exists (``request_param``, and others). You can |
| | | add third-party predicates to the list of available predicates by using one of |
| | | :meth:`pyramid.config.Configurator.add_view_predicate` or |
| | | :meth:`pyramid.config.Configurator.add_route_predicate`. The former adds a |
| | | view predicate, the latter a route predicate. |
| | | |
| | |
| | | The second argument is a view or route predicate factory, or a :term:`dotted |
| | | Python name` which refers to a view or route predicate factory. A view or |
| | | route predicate factory is most often a class with a constructor |
| | | (``__init__``), a ``text`` method, a ``phash`` method and a ``__call__`` |
| | | (``__init__``), a ``text`` method, a ``phash`` method, and a ``__call__`` |
| | | method. For example: |
| | | |
| | | .. code-block:: python |
| | |
| | | |
| | | The constructor of a predicate factory takes two arguments: ``val`` and |
| | | ``config``. The ``val`` argument will be the argument passed to |
| | | ``view_config`` (or ``add_view``). In the example above, it will be the |
| | | string ``File``. The second arg, ``config`` will be the Configurator |
| | | instance at the time of configuration. |
| | | ``view_config`` (or ``add_view``). In the example above, it will be the string |
| | | ``File``. The second argument, ``config``, will be the Configurator instance |
| | | at the time of configuration. |
| | | |
| | | The ``text`` method must return a string. It should be useful to describe |
| | | the behavior of the predicate in error messages. |
| | | The ``text`` method must return a string. It should be useful to describe the |
| | | behavior of the predicate in error messages. |
| | | |
| | | The ``phash`` method must return a string or a sequence of strings. It's |
| | | most often the same as ``text``, as long as ``text`` uniquely describes the |
| | | predicate's name and the value passed to the constructor. If ``text`` is |
| | | more general, or doesn't describe things that way, ``phash`` should return a |
| | | string with the name and the value serialized. The result of ``phash`` is |
| | | not seen in output anywhere, it just informs the uniqueness constraints for |
| | | view configuration. |
| | | The ``phash`` method must return a string or a sequence of strings. It's most |
| | | often the same as ``text``, as long as ``text`` uniquely describes the |
| | | predicate's name and the value passed to the constructor. If ``text`` is more |
| | | general, or doesn't describe things that way, ``phash`` should return a string |
| | | with the name and the value serialized. The result of ``phash`` is not seen in |
| | | output anywhere, it just informs the uniqueness constraints for view |
| | | configuration. |
| | | |
| | | The ``__call__`` method of a predicate factory must accept a resource |
| | | (``context``) and a request, and must return ``True`` or ``False``. It is |
| | | the "meat" of the predicate. |
| | | (``context``) and a request, and must return ``True`` or ``False``. It is the |
| | | "meat" of the predicate. |
| | | |
| | | You can use the same predicate factory as both a view predicate and as a |
| | | route predicate, but you'll need to call ``add_view_predicate`` and |
| | | You can use the same predicate factory as both a view predicate and as a route |
| | | predicate, but you'll need to call ``add_view_predicate`` and |
| | | ``add_route_predicate`` separately with the same factory. |
| | | |
| | | .. _subscriber_predicates: |
| | |
| | | Subscriber Predicates |
| | | ~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Subscriber predicates work almost exactly like view and route predicates. |
| | | They narrow the set of circumstances in which a subscriber will be called. |
| | | There are several minor differences between a subscriber predicate and a |
| | | view/route predicate: |
| | | Subscriber predicates work almost exactly like view and route predicates. They |
| | | narrow the set of circumstances in which a subscriber will be called. There are |
| | | several minor differences between a subscriber predicate and a view or route |
| | | predicate: |
| | | |
| | | - There are no default subscriber predicates. You must register one to use |
| | | one. |
| | | |
| | | - The ``__call__`` method of a subscriber predicate accepts a single |
| | | ``event`` object instead of a ``context`` and a ``request``. |
| | | - The ``__call__`` method of a subscriber predicate accepts a single ``event`` |
| | | object instead of a ``context`` and a ``request``. |
| | | |
| | | - Not every subscriber predicate can be used with every event type. Some |
| | | subscriber predicates will assume a certain event type. |
| | |
| | | |
| | | Once a subscriber predicate is registered, you can use it in a call to |
| | | :meth:`pyramid.config.Configurator.add_subscriber` or to |
| | | :class:`pyramid.events.subscriber`. Here's an example of using the |
| | | previously registered ``request_path_startswith`` predicate in a call to |
| | | :class:`pyramid.events.subscriber`. Here's an example of using the previously |
| | | registered ``request_path_startswith`` predicate in a call to |
| | | :meth:`~pyramid.config.Configurator.add_subscriber`: |
| | | |
| | | .. code-block:: python |
| | |
| | | |
| | | # and at configuration time |
| | | |
| | | config.add_subscriber(yosubscriber, NewRequest, |
| | | config.add_subscriber(yosubscriber, NewRequest, |
| | | request_path_startswith='/add_yo') |
| | | |
| | | Here's the same subscriber/predicate/event-type combination used via |
| | |
| | | def yosubscriber(event): |
| | | event.request.yo = 'YO!' |
| | | |
| | | In either of the above configurations, the ``yosubscriber`` callable will |
| | | only be called if the request path starts with ``/add_yo``. Otherwise the |
| | | event subscriber will not be called. |
| | | In either of the above configurations, the ``yosubscriber`` callable will only |
| | | be called if the request path starts with ``/add_yo``. Otherwise the event |
| | | subscriber will not be called. |
| | | |
| | | Note that the ``request_path_startswith`` subscriber you defined can be used |
| | | with events that have a ``request`` attribute, but not ones that do not. So, |
| | | for example, the predicate can be used with subscribers registered for |
| | | :class:`pyramid.events.NewRequest` and :class:`pyramid.events.ContextFound` |
| | | events, but it cannot be used with subscribers registered for |
| | | :class:`pyramid.events.ApplicationCreated` because the latter type of event |
| | | has no ``request`` attribute. The point being: unlike route and view |
| | | predicates, not every type of subscriber predicate will necessarily be |
| | | applicable for use in every subscriber registration. It is not the |
| | | responsibility of the predicate author to make every predicate make sense for |
| | | every event type; it is the responsibility of the predicate consumer to use |
| | | predicates that make sense for a particular event type registration. |
| | | |
| | | |
| | | |
| | | :class:`pyramid.events.ApplicationCreated` because the latter type of event has |
| | | no ``request`` attribute. The point being, unlike route and view predicates, |
| | | not every type of subscriber predicate will necessarily be applicable for use |
| | | in every subscriber registration. It is not the responsibility of the |
| | | predicate author to make every predicate make sense for every event type; it is |
| | | the responsibility of the predicate consumer to use predicates that make sense |
| | | for a particular event type registration. |
| | |
| | | .. warning:: |
| | | |
| | | Reasoning about the behavior of a "hybrid" URL dispatch + traversal |
| | | application can be challenging. To successfully reason about using |
| | | URL dispatch and traversal together, you need to understand URL |
| | | pattern matching, root factories, and the :term:`traversal` |
| | | algorithm, and the potential interactions between them. Therefore, |
| | | we don't recommend creating an application that relies on hybrid |
| | | behavior unless you must. |
| | | application can be challenging. To successfully reason about using URL |
| | | dispatch and traversal together, you need to understand URL pattern |
| | | matching, root factories, and the :term:`traversal` algorithm, and the |
| | | potential interactions between them. Therefore, we don't recommend creating |
| | | an application that relies on hybrid behavior unless you must. |
| | | |
| | | A Review of Non-Hybrid Applications |
| | | ----------------------------------- |
| | | |
| | | When used according to the tutorials in its documentation |
| | | :app:`Pyramid` is a "dual-mode" framework: the tutorials explain |
| | | how to create an application in terms of using either :term:`url |
| | | dispatch` *or* :term:`traversal`. This chapter details how you might |
| | | combine these two dispatch mechanisms, but we'll review how they work |
| | | in isolation before trying to combine them. |
| | | When used according to the tutorials in its documentation, :app:`Pyramid` is a |
| | | "dual-mode" framework: the tutorials explain how to create an application in |
| | | terms of using either :term:`URL dispatch` *or* :term:`traversal`. This |
| | | chapter details how you might combine these two dispatch mechanisms, but we'll |
| | | review how they work in isolation before trying to combine them. |
| | | |
| | | URL Dispatch Only |
| | | ~~~~~~~~~~~~~~~~~ |
| | | |
| | | An application that uses :term:`url dispatch` exclusively to map URLs to code |
| | | will often have statements like this within application startup |
| | | An application that uses :term:`URL dispatch` exclusively to map URLs to code |
| | | will often have statements like this within its application startup |
| | | configuration: |
| | | |
| | | .. code-block:: python |
| | |
| | | config.add_view('myproject.views.bazbuz', route_name='bazbuz') |
| | | |
| | | Each :term:`route` corresponds to one or more view callables. Each view |
| | | callable is associated with a route by passing a ``route_name`` parameter |
| | | that matches its name during a call to |
| | | :meth:`~pyramid.config.Configurator.add_view`. When a route is matched |
| | | during a request, :term:`view lookup` is used to match the request to its |
| | | associated view callable. The presence of calls to |
| | | callable is associated with a route by passing a ``route_name`` parameter that |
| | | matches its name during a call to |
| | | :meth:`~pyramid.config.Configurator.add_view`. When a route is matched during |
| | | a request, :term:`view lookup` is used to match the request to its associated |
| | | view callable. The presence of calls to |
| | | :meth:`~pyramid.config.Configurator.add_route` signify that an application is |
| | | using URL dispatch. |
| | | |
| | |
| | | |
| | | When the above configuration is applied to an application, the |
| | | ``mypackage.views.foobar`` view callable above will be called when the URL |
| | | ``/foobar`` is visited. Likewise, the view ``mypackage.views.bazbuz`` will |
| | | be called when the URL ``/bazbuz`` is visited. |
| | | ``/foobar`` is visited. Likewise, the view ``mypackage.views.bazbuz`` will be |
| | | called when the URL ``/bazbuz`` is visited. |
| | | |
| | | Typically, an application that uses traversal exclusively won't perform any |
| | | calls to :meth:`pyramid.config.Configurator.add_route` in its startup |
| | | code. |
| | | calls to :meth:`pyramid.config.Configurator.add_route` in its startup code. |
| | | |
| | | .. index:: |
| | | single: hybrid applications |
| | |
| | | Hybrid Applications |
| | | ------------------- |
| | | |
| | | Either traversal or url dispatch alone can be used to create a |
| | | :app:`Pyramid` application. However, it is also possible to |
| | | combine the concepts of traversal and url dispatch when building an |
| | | application: the result is a hybrid application. In a hybrid |
| | | application, traversal is performed *after* a particular route has |
| | | matched. |
| | | Either traversal or URL dispatch alone can be used to create a :app:`Pyramid` |
| | | application. However, it is also possible to combine the concepts of traversal |
| | | and URL dispatch when building an application, the result of which is a hybrid |
| | | application. In a hybrid application, traversal is performed *after* a |
| | | particular route has matched. |
| | | |
| | | A hybrid application is a lot more like a "pure" traversal-based |
| | | application than it is like a "pure" URL-dispatch based application. |
| | | But unlike in a "pure" traversal-based application, in a hybrid |
| | | application, :term:`traversal` is performed during a request after a |
| | | route has already matched. This means that the URL pattern that |
| | | represents the ``pattern`` argument of a route must match the |
| | | ``PATH_INFO`` of a request, and after the route pattern has matched, |
| | | most of the "normal" rules of traversal with respect to :term:`resource |
| | | location` and :term:`view lookup` apply. |
| | | A hybrid application is a lot more like a "pure" traversal-based application |
| | | than it is like a "pure" URL-dispatch based application. But unlike in a "pure" |
| | | traversal-based application, in a hybrid application :term:`traversal` is |
| | | performed during a request after a route has already matched. This means that |
| | | the URL pattern that represents the ``pattern`` argument of a route must match |
| | | the ``PATH_INFO`` of a request, and after the route pattern has matched, most |
| | | of the "normal" rules of traversal with respect to :term:`resource location` |
| | | and :term:`view lookup` apply. |
| | | |
| | | There are only four real differences between a purely traversal-based |
| | | application and a hybrid application: |
| | | |
| | | - In a purely traversal based application, no routes are defined; in a |
| | | hybrid application, at least one route will be defined. |
| | | - In a purely traversal-based application, no routes are defined. In a hybrid |
| | | application, at least one route will be defined. |
| | | |
| | | - In a purely traversal based application, the root object used is |
| | | global, implied by the :term:`root factory` provided at startup |
| | | time; in a hybrid application, the :term:`root` object at which |
| | | traversal begins may be varied on a per-route basis. |
| | | - In a purely traversal-based application, the root object used is global, |
| | | implied by the :term:`root factory` provided at startup time. In a hybrid |
| | | application, the :term:`root` object at which traversal begins may be varied |
| | | on a per-route basis. |
| | | |
| | | - In a purely traversal-based application, the ``PATH_INFO`` of the |
| | | underlying :term:`WSGI` environment is used wholesale as a traversal |
| | | path; in a hybrid application, the traversal path is not the entire |
| | | ``PATH_INFO`` string, but a portion of the URL determined by a |
| | | matching pattern in the matched route configuration's pattern. |
| | | - In a purely traversal-based application, the ``PATH_INFO`` of the underlying |
| | | :term:`WSGI` environment is used wholesale as a traversal path. In a hybrid |
| | | application, the traversal path is not the entire ``PATH_INFO`` string, but a |
| | | portion of the URL determined by a matching pattern in the matched route |
| | | configuration's pattern. |
| | | |
| | | - In a purely traversal based application, view configurations which |
| | | do not mention a ``route_name`` argument are considered during |
| | | :term:`view lookup`; in a hybrid application, when a route is |
| | | matched, only view configurations which mention that route's name as |
| | | a ``route_name`` are considered during :term:`view lookup`. |
| | | - In a purely traversal-based application, view configurations which do not |
| | | mention a ``route_name`` argument are considered during :term:`view lookup`. |
| | | In a hybrid application, when a route is matched, only view configurations |
| | | which mention that route's name as a ``route_name`` are considered during |
| | | :term:`view lookup`. |
| | | |
| | | More generally, a hybrid application *is* a traversal-based |
| | | application except: |
| | | More generally, a hybrid application *is* a traversal-based application except: |
| | | |
| | | - the traversal *root* is chosen based on the route configuration of |
| | | the route that matched instead of from the ``root_factory`` supplied |
| | | during application startup configuration. |
| | | - the traversal *root* is chosen based on the route configuration of the route |
| | | that matched, instead of from the ``root_factory`` supplied during |
| | | application startup configuration. |
| | | |
| | | - the traversal *path* is chosen based on the route configuration of |
| | | the route that matched rather than from the ``PATH_INFO`` of a |
| | | request. |
| | | - the traversal *path* is chosen based on the route configuration of the route |
| | | that matched, rather than from the ``PATH_INFO`` of a request. |
| | | |
| | | - the set of views that may be chosen during :term:`view lookup` when |
| | | a route matches are limited to those which specifically name a |
| | | ``route_name`` in their configuration that is the same as the |
| | | matched route's ``name``. |
| | | - the set of views that may be chosen during :term:`view lookup` when a route |
| | | matches are limited to those which specifically name a ``route_name`` in |
| | | their configuration that is the same as the matched route's ``name``. |
| | | |
| | | To create a hybrid mode application, use a :term:`route configuration` |
| | | that implies a particular :term:`root factory` and which also includes |
| | | a ``pattern`` argument that contains a special dynamic part: either |
| | | ``*traverse`` or ``*subpath``. |
| | | To create a hybrid mode application, use a :term:`route configuration` that |
| | | implies a particular :term:`root factory` and which also includes a ``pattern`` |
| | | argument that contains a special dynamic part: either ``*traverse`` or |
| | | ``*subpath``. |
| | | |
| | | The Root Object for a Route Match |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | A hybrid application implies that traversal is performed during a |
| | | request after a route has matched. Traversal, by definition, must |
| | | always begin at a root object. Therefore it's important to know |
| | | *which* root object will be traversed after a route has matched. |
| | | A hybrid application implies that traversal is performed during a request after |
| | | a route has matched. Traversal, by definition, must always begin at a root |
| | | object. Therefore it's important to know *which* root object will be traversed |
| | | after a route has matched. |
| | | |
| | | Figuring out which :term:`root` object results from a particular route |
| | | match is straightforward. When a route is matched: |
| | | Figuring out which :term:`root` object results from a particular route match is |
| | | straightforward. When a route is matched: |
| | | |
| | | - If the route's configuration has a ``factory`` argument which |
| | | points to a :term:`root factory` callable, that callable will be |
| | | called to generate a :term:`root` object. |
| | | - If the route's configuration has a ``factory`` argument which points to a |
| | | :term:`root factory` callable, that callable will be called to generate a |
| | | :term:`root` object. |
| | | |
| | | - If the route's configuration does not have a ``factory`` |
| | | argument, the *global* :term:`root factory` will be called to |
| | | generate a :term:`root` object. The global root factory is the |
| | | callable implied by the ``root_factory`` argument passed to the |
| | | :class:`~pyramid.config.Configurator` at application |
| | | startup time. |
| | | - If the route's configuration does not have a ``factory`` argument, the |
| | | *global* :term:`root factory` will be called to generate a :term:`root` |
| | | object. The global root factory is the callable implied by the |
| | | ``root_factory`` argument passed to the :class:`~pyramid.config.Configurator` |
| | | at application startup time. |
| | | |
| | | - If a ``root_factory`` argument is not provided to the |
| | | :class:`~pyramid.config.Configurator` at startup time, a |
| | | *default* root factory is used. The default root factory is used to |
| | | generate a root object. |
| | | :class:`~pyramid.config.Configurator` at startup time, a *default* root |
| | | factory is used. The default root factory is used to generate a root object. |
| | | |
| | | .. note:: |
| | | |
| | | Root factories related to a route were explained previously within |
| | | :ref:`route_factories`. Both the global root factory and default |
| | | root factory were explained previously within |
| | | :ref:`the_resource_tree`. |
| | | :ref:`route_factories`. Both the global root factory and default root |
| | | factory were explained previously within :ref:`the_resource_tree`. |
| | | |
| | | .. index:: |
| | | pair: hybrid applications; *traverse route pattern |
| | | |
| | | .. _using_traverse_in_a_route_pattern: |
| | | |
| | | Using ``*traverse`` In a Route Pattern |
| | | Using ``*traverse`` in a Route Pattern |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | A hybrid application most often implies the inclusion of a route |
| | | configuration that contains the special token ``*traverse`` at the end |
| | | of a route's pattern: |
| | | A hybrid application most often implies the inclusion of a route configuration |
| | | that contains the special token ``*traverse`` at the end of a route's pattern: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | config.add_route('home', '{foo}/{bar}/*traverse') |
| | | |
| | | A ``*traverse`` token at the end of the pattern in a route's |
| | | configuration implies a "remainder" *capture* value. When it is used, |
| | | it will match the remainder of the path segments of the URL. This |
| | | remainder becomes the path used to perform traversal. |
| | | A ``*traverse`` token at the end of the pattern in a route's configuration |
| | | implies a "remainder" *capture* value. When it is used, it will match the |
| | | remainder of the path segments of the URL. This remainder becomes the path |
| | | used to perform traversal. |
| | | |
| | | .. note:: |
| | | |
| | | The ``*remainder`` route pattern syntax is explained in more |
| | | detail within :ref:`route_pattern_syntax`. |
| | | The ``*remainder`` route pattern syntax is explained in more detail within |
| | | :ref:`route_pattern_syntax`. |
| | | |
| | | A hybrid mode application relies more heavily on :term:`traversal` to do |
| | | :term:`resource location` and :term:`view lookup` than most examples indicate |
| | | within :ref:`urldispatch_chapter`. |
| | | |
| | | Because the pattern of the above route ends with ``*traverse``, when this |
| | | route configuration is matched during a request, :app:`Pyramid` will attempt |
| | | to use :term:`traversal` against the :term:`root` object implied by the |
| | | :term:`root factory` that is implied by the route's configuration. Since no |
| | | Because the pattern of the above route ends with ``*traverse``, when this route |
| | | configuration is matched during a request, :app:`Pyramid` will attempt to use |
| | | :term:`traversal` against the :term:`root` object implied by the :term:`root |
| | | factory` that is implied by the route's configuration. Since no |
| | | ``root_factory`` argument is explicitly specified for this route, this will |
| | | either be the *global* root factory for the application, or the *default* |
| | | root factory. Once :term:`traversal` has found a :term:`context` resource, |
| | | either be the *global* root factory for the application, or the *default* root |
| | | factory. Once :term:`traversal` has found a :term:`context` resource, |
| | | :term:`view lookup` will be invoked in almost exactly the same way it would |
| | | have been invoked in a "pure" traversal-based application. |
| | | |
| | | Let's assume there is no *global* :term:`root factory` configured in |
| | | this application. The *default* :term:`root factory` cannot be traversed: |
| | | it has no useful ``__getitem__`` method. So we'll need to associate |
| | | this route configuration with a custom root factory in order to |
| | | create a useful hybrid application. To that end, let's imagine that |
| | | we've created a root factory that looks like so in a module named |
| | | ``routes.py``: |
| | | Let's assume there is no *global* :term:`root factory` configured in this |
| | | application. The *default* :term:`root factory` cannot be traversed; it has no |
| | | useful ``__getitem__`` method. So we'll need to associate this route |
| | | configuration with a custom root factory in order to create a useful hybrid |
| | | application. To that end, let's imagine that we've created a root factory that |
| | | looks like so in a module named ``routes.py``: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | def root_factory(request): |
| | | return root |
| | | |
| | | Above, we've defined a (bogus) resource tree that can be traversed, and a |
| | | Above we've defined a (bogus) resource tree that can be traversed, and a |
| | | ``root_factory`` function that can be used as part of a particular route |
| | | configuration statement: |
| | | |
| | |
| | | config.add_route('home', '{foo}/{bar}/*traverse', |
| | | factory='mypackage.routes.root_factory') |
| | | |
| | | The ``factory`` above points at the function we've defined. It will return |
| | | an instance of the ``Resource`` class as a root object whenever this route is |
| | | The ``factory`` above points at the function we've defined. It will return an |
| | | instance of the ``Resource`` class as a root object whenever this route is |
| | | matched. Instances of the ``Resource`` class can be used for tree traversal |
| | | because they have a ``__getitem__`` method that does something nominally |
| | | useful. Since traversal uses ``__getitem__`` to walk the resources of a |
| | |
| | | |
| | | .. note:: |
| | | |
| | | We could have also used our ``root_factory`` function as the |
| | | ``root_factory`` argument of the |
| | | :class:`~pyramid.config.Configurator` constructor, instead |
| | | of associating it with a particular route inside the route's |
| | | configuration. Every hybrid route configuration that is matched but |
| | | which does *not* name a ``factory`` attribute will use the use |
| | | global ``root_factory`` function to generate a root object. |
| | | We could have also used our ``root_factory`` function as the ``root_factory`` |
| | | argument of the :class:`~pyramid.config.Configurator` constructor, instead of |
| | | associating it with a particular route inside the route's configuration. |
| | | Every hybrid route configuration that is matched, but which does *not* name a |
| | | ``factory`` attribute, will use the global ``root_factory`` function to |
| | | generate a root object. |
| | | |
| | | When the route configuration named ``home`` above is matched during a |
| | | request, the matchdict generated will be based on its pattern: |
| | | When the route configuration named ``home`` above is matched during a request, |
| | | the matchdict generated will be based on its pattern: |
| | | ``{foo}/{bar}/*traverse``. The "capture value" implied by the ``*traverse`` |
| | | element in the pattern will be used to traverse the resource tree in order to |
| | | find a context resource, starting from the root object returned from the root |
| | | factory. In the above example, the :term:`root` object found will be the |
| | | instance named ``root`` in ``routes.py``. |
| | | |
| | | If the URL that matched a route with the pattern ``{foo}/{bar}/*traverse``, |
| | | is ``http://example.com/one/two/a/b/c``, the traversal path used |
| | | against the root object will be ``a/b/c``. As a result, |
| | | :app:`Pyramid` will attempt to traverse through the edges ``'a'``, |
| | | ``'b'``, and ``'c'``, beginning at the root object. |
| | | If the URL that matched a route with the pattern ``{foo}/{bar}/*traverse`` is |
| | | ``http://example.com/one/two/a/b/c``, the traversal path used against the root |
| | | object will be ``a/b/c``. As a result, :app:`Pyramid` will attempt to traverse |
| | | through the edges ``'a'``, ``'b'``, and ``'c'``, beginning at the root object. |
| | | |
| | | In our above example, this particular set of traversal steps will mean that |
| | | the :term:`context` resource of the view would be the ``Resource`` object |
| | | we've named ``'c'`` in our bogus resource tree and the :term:`view name` |
| | | resulting from traversal will be the empty string; if you need a refresher |
| | | about why this outcome is presumed, see :ref:`traversal_algorithm`. |
| | | In our above example, this particular set of traversal steps will mean that the |
| | | :term:`context` resource of the view would be the ``Resource`` object we've |
| | | named ``'c'`` in our bogus resource tree, and the :term:`view name` resulting |
| | | from traversal will be the empty string. If you need a refresher about why |
| | | this outcome is presumed, see :ref:`traversal_algorithm`. |
| | | |
| | | At this point, a suitable view callable will be found and invoked |
| | | using :term:`view lookup` as described in :ref:`view_configuration`, |
| | | but with a caveat: in order for view lookup to work, we need to define |
| | | a view configuration that will match when :term:`view lookup` is |
| | | invoked after a route matches: |
| | | At this point, a suitable view callable will be found and invoked using |
| | | :term:`view lookup` as described in :ref:`view_configuration`, but with a |
| | | caveat: in order for view lookup to work, we need to define a view |
| | | configuration that will match when :term:`view lookup` is invoked after a route |
| | | matches: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | factory='mypackage.routes.root_factory') |
| | | config.add_view('mypackage.views.myview', route_name='home') |
| | | |
| | | Note that the above call to |
| | | :meth:`~pyramid.config.Configurator.add_view` includes a ``route_name`` |
| | | argument. View configurations that include a ``route_name`` argument are |
| | | meant to associate a particular view declaration with a route, using the |
| | | route's name, in order to indicate that the view should *only be invoked when |
| | | the route matches*. |
| | | Note that the above call to :meth:`~pyramid.config.Configurator.add_view` |
| | | includes a ``route_name`` argument. View configurations that include a |
| | | ``route_name`` argument are meant to associate a particular view declaration |
| | | with a route, using the route's name, in order to indicate that the view should |
| | | *only be invoked when the route matches*. |
| | | |
| | | Calls to :meth:`~pyramid.config.Configurator.add_view` may pass a |
| | | ``route_name`` attribute, which refers to the value of an existing route's |
| | | ``name`` argument. In the above example, the route name is ``home``, |
| | | referring to the name of the route defined above it. |
| | | ``name`` argument. In the above example, the route name is ``home``, referring |
| | | to the name of the route defined above it. |
| | | |
| | | The above ``mypackage.views.myview`` view callable will be invoked when: |
| | | The above ``mypackage.views.myview`` view callable will be invoked when the |
| | | following conditions are met: |
| | | |
| | | - the route named "home" is matched |
| | | - The route named "home" is matched. |
| | | |
| | | - the :term:`view name` resulting from traversal is the empty string. |
| | | - The :term:`view name` resulting from traversal is the empty string. |
| | | |
| | | - the :term:`context` resource is any object. |
| | | - The :term:`context` resource is any object. |
| | | |
| | | It is also possible to declare alternate views that may be invoked |
| | | when a hybrid route is matched: |
| | | It is also possible to declare alternative views that may be invoked when a |
| | | hybrid route is matched: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | name='another') |
| | | |
| | | The ``add_view`` call for ``mypackage.views.another_view`` above names a |
| | | different view and, more importantly, a different :term:`view name`. The |
| | | above ``mypackage.views.another_view`` view will be invoked when: |
| | | different view and, more importantly, a different :term:`view name`. The above |
| | | ``mypackage.views.another_view`` view will be invoked when the following |
| | | conditions are met: |
| | | |
| | | - the route named "home" is matched |
| | | - The route named "home" is matched. |
| | | |
| | | - the :term:`view name` resulting from traversal is ``another``. |
| | | - The :term:`view name` resulting from traversal is ``another``. |
| | | |
| | | - the :term:`context` resource is any object. |
| | | - The :term:`context` resource is any object. |
| | | |
| | | For instance, if the URL ``http://example.com/one/two/a/another`` is provided |
| | | to an application that uses the previously mentioned resource tree, the |
| | | ``mypackage.views.another`` view callable will be called instead of the |
| | | ``mypackage.views.myview`` view callable because the :term:`view name` will |
| | | be ``another`` instead of the empty string. |
| | | ``mypackage.views.another_view`` view callable will be called instead of the |
| | | ``mypackage.views.myview`` view callable because the :term:`view name` will be |
| | | ``another`` instead of the empty string. |
| | | |
| | | More complicated matching can be composed. All arguments to *route* |
| | | configuration statements and *view* configuration statements are |
| | | supported in hybrid applications (such as :term:`predicate` |
| | | arguments). |
| | | configuration statements and *view* configuration statements are supported in |
| | | hybrid applications (such as :term:`predicate` arguments). |
| | | |
| | | Using the ``traverse`` Argument In a Route Definition |
| | | Using the ``traverse`` Argument in a Route Definition |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Rather than using the ``*traverse`` remainder marker in a pattern, you |
| | | can use the ``traverse`` argument to the |
| | | :meth:`~pyramid.config.Configurator.add_route` method. |
| | | Rather than using the ``*traverse`` remainder marker in a pattern, you can use |
| | | the ``traverse`` argument to the :meth:`~pyramid.config.Configurator.add_route` |
| | | method. |
| | | |
| | | When you use the ``*traverse`` remainder marker, the traversal path is |
| | | limited to being the remainder segments of a request URL when a route |
| | | matches. However, when you use the ``traverse`` argument or |
| | | attribute, you have more control over how to compose a traversal path. |
| | | When you use the ``*traverse`` remainder marker, the traversal path is limited |
| | | to being the remainder segments of a request URL when a route matches. |
| | | However, when you use the ``traverse`` argument or attribute, you have more |
| | | control over how to compose a traversal path. |
| | | |
| | | Here's a use of the ``traverse`` pattern in a call to |
| | | :meth:`~pyramid.config.Configurator.add_route`: |
| | |
| | | config.add_route('abc', '/articles/{article}/edit', |
| | | traverse='/{article}') |
| | | |
| | | The syntax of the ``traverse`` argument is the same as it is for |
| | | ``pattern``. |
| | | The syntax of the ``traverse`` argument is the same as it is for ``pattern``. |
| | | |
| | | If, as above, the ``pattern`` provided is ``/articles/{article}/edit``, |
| | | and the ``traverse`` argument provided is ``/{article}``, when a |
| | | request comes in that causes the route to match in such a way that the |
| | | ``article`` match value is ``1`` (when the request URI is |
| | | ``/articles/1/edit``), the traversal path will be generated as ``/1``. |
| | | This means that the root object's ``__getitem__`` will be called with |
| | | the name ``1`` during the traversal phase. If the ``1`` object |
| | | exists, it will become the :term:`context` of the request. |
| | | The :ref:`traversal_chapter` chapter has more information about traversal. |
| | | If, as above, the ``pattern`` provided is ``/articles/{article}/edit``, and the |
| | | ``traverse`` argument provided is ``/{article}``, when a request comes in that |
| | | causes the route to match in such a way that the ``article`` match value is |
| | | ``1`` (when the request URI is ``/articles/1/edit``), the traversal path will |
| | | be generated as ``/1``. This means that the root object's ``__getitem__`` will |
| | | be called with the name ``1`` during the traversal phase. If the ``1`` object |
| | | exists, it will become the :term:`context` of the request. The |
| | | :ref:`traversal_chapter` chapter has more information about traversal. |
| | | |
| | | If the traversal path contains segment marker names which are not |
| | | present in the pattern argument, a runtime error will occur. The |
| | | ``traverse`` pattern should not contain segment markers that do not |
| | | exist in the ``path``. |
| | | If the traversal path contains segment marker names which are not present in |
| | | the pattern argument, a runtime error will occur. The ``traverse`` pattern |
| | | should not contain segment markers that do not exist in the ``path``. |
| | | |
| | | Note that the ``traverse`` argument is ignored when attached to a |
| | | route that has a ``*traverse`` remainder marker in its pattern. |
| | | Note that the ``traverse`` argument is ignored when attached to a route that |
| | | has a ``*traverse`` remainder marker in its pattern. |
| | | |
| | | Traversal will begin at the root object implied by this route (either |
| | | the global root, or the object returned by the ``factory`` associated |
| | | with this route). |
| | | Traversal will begin at the root object implied by this route (either the |
| | | global root, or the object returned by the ``factory`` associated with this |
| | | route). |
| | | |
| | | .. index:: |
| | | pair: hybrid applications; global views |
| | |
| | | Making Global Views Match |
| | | +++++++++++++++++++++++++ |
| | | |
| | | By default, only view configurations that mention a ``route_name`` |
| | | will be found during view lookup when a route that has a ``*traverse`` |
| | | in its pattern matches. You can allow views without a ``route_name`` |
| | | attribute to match a route by adding the ``use_global_views`` flag to |
| | | the route definition. For example, the ``myproject.views.bazbuz`` |
| | | view below will be found if the route named ``abc`` below is matched |
| | | and the ``PATH_INFO`` is ``/abc/bazbuz``, even though the view |
| | | configuration statement does not have the ``route_name="abc"`` |
| | | By default, only view configurations that mention a ``route_name`` will be |
| | | found during view lookup when a route that has a ``*traverse`` in its pattern |
| | | matches. You can allow views without a ``route_name`` attribute to match a |
| | | route by adding the ``use_global_views`` flag to the route definition. For |
| | | example, the ``myproject.views.bazbuz`` view below will be found if the route |
| | | named ``abc`` below is matched and the ``PATH_INFO`` is ``/abc/bazbuz``, even |
| | | though the view configuration statement does not have the ``route_name="abc"`` |
| | | attribute. |
| | | |
| | | .. code-block:: python |
| | |
| | | from the request's subpath when its ``use_subpath`` argument is ``True``, so |
| | | it's useful to be able to influence this value. |
| | | |
| | | When ``*subpath`` exists in a pattern, no path is actually traversed, |
| | | but the traversal algorithm will return a :term:`subpath` list implied |
| | | by the capture value of ``*subpath``. You'll see this pattern most |
| | | commonly in route declarations that look like this: |
| | | When ``*subpath`` exists in a pattern, no path is actually traversed, but the |
| | | traversal algorithm will return a :term:`subpath` list implied by the capture |
| | | value of ``*subpath``. You'll see this pattern most commonly in route |
| | | declarations that look like this: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | config.add_route('static', '/static/*subpath') |
| | | config.add_view(www, route_name='static') |
| | | |
| | | ``mypackage.views.www`` is an instance of |
| | | :class:`pyramid.static.static_view`. This effectively tells the static |
| | | helper to traverse everything in the subpath as a filename. |
| | | ``mypackage.views.www`` is an instance of :class:`pyramid.static.static_view`. |
| | | This effectively tells the static helper to traverse everything in the subpath |
| | | as a filename. |
| | | |
| | | |
| | | .. index:: |
| | | pair: hybrid applications; corner cases |
| | | |
| | | Corner Cases |
| | | ------------ |
| | | |
| | | A number of corner case "gotchas" exist when using a hybrid |
| | | application. We'll detail them here. |
| | | |
| | | Registering a Default View for a Route That Has a ``view`` Attribute |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | .. warning:: As of :app:`Pyramid` 1.1 this section is slated to be removed in |
| | | a later documentation release because the ability to add views |
| | | directly to the :term:`route configuration` by passing a ``view`` argument |
| | | to ``add_route`` has been deprecated. |
| | | |
| | | It is an error to provide *both* a ``view`` argument to a :term:`route |
| | | configuration` *and* a :term:`view configuration` which names a |
| | | ``route_name`` that has no ``name`` value or the empty ``name`` value. For |
| | | example, this pair of declarations will generate a conflict error at startup |
| | | time. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | config.add_route('home', '{foo}/{bar}/*traverse', |
| | | view='myproject.views.home') |
| | | config.add_view('myproject.views.another', route_name='home') |
| | | |
| | | This is because the ``view`` argument to the |
| | | :meth:`~pyramid.config.Configurator.add_route` above is an *implicit* |
| | | default view when that route matches. ``add_route`` calls don't *need* to |
| | | supply a view attribute. For example, this ``add_route`` call: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | config.add_route('home', '{foo}/{bar}/*traverse', |
| | | view='myproject.views.home') |
| | | |
| | | Can also be spelled like so: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | config.add_route('home', '{foo}/{bar}/*traverse') |
| | | config.add_view('myproject.views.home', route_name='home') |
| | | |
| | | The two spellings are logically equivalent. In fact, the former is just a |
| | | syntactical shortcut for the latter. |
| | | |
| | | Binding Extra Views Against a Route Configuration that Doesn't Have a ``*traverse`` Element In Its Pattern |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Here's another corner case that just makes no sense: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | config.add_route('abc', '/abc', view='myproject.views.abc') |
| | | config.add_view('myproject.views.bazbuz', name='bazbuz', |
| | | route_name='abc') |
| | | |
| | | The above view declaration is useless, because it will never be matched when |
| | | the route it references has matched. Only the view associated with the route |
| | | itself (``myproject.views.abc``) will ever be invoked when the route matches, |
| | | because the default view is always invoked when a route matches and when no |
| | | post-match traversal is performed. |
| | | |
| | | To make the above view declaration useful, the special ``*traverse`` |
| | | token must end the route's pattern. For example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | config.add_route('abc', '/abc/*traverse', view='myproject.views.abc') |
| | | config.add_view('myproject.views.bazbuz', name='bazbuz', |
| | | route_name='abc') |
| | | |
| | | With the above configuration, the ``myproject.views.bazbuz`` view will |
| | | be invoked when the request URI is ``/abc/bazbuz``, assuming there is |
| | | no object contained by the root object with the key ``bazbuz``. A |
| | | different request URI, such as ``/abc/foo/bar``, would invoke the |
| | | default ``myproject.views.abc`` view. |
| | | |
| | | .. index:: |
| | | pair: hybrid urls; generating |
| | | pair: hybrid URLs; generating |
| | | |
| | | .. _generating_hybrid_urls: |
| | | |
| | |
| | | |
| | | .. versionadded:: 1.5 |
| | | |
| | | The :meth:`pyramid.request.Request.resource_url` method and the |
| | | :meth:`pyramid.request.Request.resource_path` method both accept optional |
| | | The :meth:`pyramid.request.Request.resource_url` method and the |
| | | :meth:`pyramid.request.Request.resource_path` method both accept optional |
| | | keyword arguments that make it easier to generate route-prefixed URLs that |
| | | contain paths to traversal resources:``route_name``, ``route_kw``, and |
| | | contain paths to traversal resources: ``route_name``, ``route_kw``, and |
| | | ``route_remainder_name``. |
| | | |
| | | Any route that has a pattern that contains a ``*remainder`` pattern (any |
| | | stararg remainder pattern, such as ``*traverse`` or ``*subpath`` or ``*fred``) |
| | | can be used as the target name for ``request.resource_url(..., route_name=)`` |
| | | and ``request.resource_path(..., route_name=)``. |
| | | stararg remainder pattern, such as ``*traverse``, ``*subpath``, or ``*fred``) |
| | | can be used as the target name for ``request.resource_url(..., route_name=)`` |
| | | and ``request.resource_path(..., route_name=)``. |
| | | |
| | | For example, let's imagine you have a route defined in your Pyramid application |
| | | like so: |
| | |
| | | |
| | | config.add_route('mysection', '/mysection*traverse') |
| | | |
| | | If you'd like to generate the URL ``http://example.com/mysection/a/``, you can |
| | | If you'd like to generate the URL ``http://example.com/mysection/a/``, you can |
| | | use the following incantation, assuming that the variable ``a`` below points to |
| | | a resource that is a child of the root with a ``__name__`` of ``a``: |
| | | |
| | |
| | | |
| | | request.resource_path(a, route_name='mysection') |
| | | |
| | | The path is virtual host aware, so if the ``X-Vhm-Root`` environ variable is |
| | | present in the request, and it's set to ``/a``, the above call to |
| | | ``request.resource_url`` would generate ``http://example.com/mysection/`` |
| | | and the above call to ``request.resource_path`` would generate ``/mysection/``. |
| | | See :ref:`virtual_root_support` for more information. |
| | | The path is virtual host aware, so if the ``X-Vhm-Root`` environment variable |
| | | is present in the request, and it's set to ``/a``, the above call to |
| | | ``request.resource_url`` would generate ``http://example.com/mysection/``, and |
| | | the above call to ``request.resource_path`` would generate ``/mysection/``. See |
| | | :ref:`virtual_root_support` for more information. |
| | | |
| | | If the route you're trying to use needs simple dynamic part values to be filled |
| | | in to succesfully generate the URL, you can pass these as the ``route_kw`` |
| | | in to succesfully generate the URL, you can pass these as the ``route_kw`` |
| | | argument to ``resource_url`` and ``resource_path``. For example, assuming that |
| | | the route definition is like so: |
| | | |
| | |
| | | |
| | | request.resource_url(a, route_name='mysection', route_kw={'id':'1'}) |
| | | |
| | | If you pass ``route_kw`` but do not pass ``route_name``, ``route_kw`` will |
| | | be ignored. |
| | | If you pass ``route_kw`` but do not pass ``route_name``, ``route_kw`` will be |
| | | ignored. |
| | | |
| | | By default this feature works by calling ``route_url`` under the hood, |
| | | and passing the value of the resource path to that function as ``traverse``. |
| | | If your route has a different ``*stararg`` remainder name (such as |
| | | ``*subpath``), you can tell ``resource_url`` or ``resource_path`` to use that |
| | | instead of ``traverse`` by passing ``route_remainder_name``. For example, |
| | | if you have the following route: |
| | | By default this feature works by calling ``route_url`` under the hood, and |
| | | passing the value of the resource path to that function as ``traverse``. If |
| | | your route has a different ``*stararg`` remainder name (such as ``*subpath``), |
| | | you can tell ``resource_url`` or ``resource_path`` to use that instead of |
| | | ``traverse`` by passing ``route_remainder_name``. For example, if you have the |
| | | following route: |
| | | |
| | | .. code-block:: python |
| | | |
| | |
| | | |
| | | .. code-block:: python |
| | | |
| | | request.resource_path(a, route_name='mysection', |
| | | request.resource_path(a, route_name='mysection', |
| | | route_remainder_name='subpath') |
| | | |
| | | If you pass ``route_remainder_name`` but do not pass ``route_name``, |
| | | If you pass ``route_remainder_name`` but do not pass ``route_name``, |
| | | ``route_remainder_name`` will be ignored. |
| | | |
| | | If you try to use ``resource_path`` or ``resource_url`` when the ``route_name`` |
| | |
| | | will not be raised, but the generated URL will not contain any remainder |
| | | information either. |
| | | |
| | | All other values that are normally passable to ``resource_path`` and |
| | | ``resource_url`` (such as ``query``, ``anchor``, ``host``, ``port``, and |
| | | All other values that are normally passable to ``resource_path`` and |
| | | ``resource_url`` (such as ``query``, ``anchor``, ``host``, ``port``, and |
| | | positional elements) work as you might expect in this configuration. |
| | | |
| | | Note that this feature is incompatible with the ``__resource_url__`` feature |
| | | (see :ref:`overriding_resource_url_generation`) implemented on resource |
| | | objects. Any ``__resource_url__`` supplied by your resource will be ignored |
| | | when you pass ``route_name``. |
| | | |
| | |
| | | Using the Introspector |
| | | ---------------------- |
| | | |
| | | Here's an example of using Pyramid's introspector from within a view |
| | | callable: |
| | | Here's an example of using Pyramid's introspector from within a view callable: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | route_intr = introspector.get('routes', route_name) |
| | | return Response(str(route_intr['pattern'])) |
| | | |
| | | This view will return a response that contains the "pattern" argument |
| | | provided to the ``add_route`` method of the route which matched when the view |
| | | was called. It uses the :meth:`pyramid.interfaces.IIntrospector.get` method |
| | | to return an introspectable in the category ``routes`` with a |
| | | This view will return a response that contains the "pattern" argument provided |
| | | to the ``add_route`` method of the route which matched when the view was |
| | | called. It uses the :meth:`pyramid.interfaces.IIntrospector.get` method to |
| | | return an introspectable in the category ``routes`` with a |
| | | :term:`discriminator` equal to the matched route name. It then uses the |
| | | returned introspectable to obtain a "pattern" value. |
| | | |
| | |
| | | Introspectable Objects |
| | | ---------------------- |
| | | |
| | | Introspectable objects are returned from query methods of an introspector. |
| | | Each introspectable object implements the attributes and methods |
| | | documented at :class:`pyramid.interfaces.IIntrospectable`. |
| | | Introspectable objects are returned from query methods of an introspector. Each |
| | | introspectable object implements the attributes and methods documented at |
| | | :class:`pyramid.interfaces.IIntrospectable`. |
| | | |
| | | The important attributes shared by all introspectables are the following: |
| | | |
| | |
| | | |
| | | ``discriminator`` |
| | | |
| | | A hashable object representing the unique value of this introspectable |
| | | within its category. |
| | | A hashable object representing the unique value of this introspectable within |
| | | its category. |
| | | |
| | | ``discriminator_hash`` |
| | | |
| | | The integer hash of the discriminator (useful for using in HTML links). |
| | | The integer hash of the discriminator (useful in HTML links). |
| | | |
| | | ``type_name`` |
| | | |
| | |
| | | |
| | | ``action_info`` |
| | | |
| | | An object describing the directive call site which caused this |
| | | introspectable to be registered; contains attributes described in |
| | | An object describing the directive call site which caused this introspectable |
| | | to be registered. It contains attributes described in |
| | | :class:`pyramid.interfaces.IActionInfo`. |
| | | |
| | | Besides having the attributes described above, an introspectable is a |
| | |
| | | |
| | | Each introspectable in the ``subscribers`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.add_subscriber` (or the decorator |
| | | equivalent); each will have the following data. |
| | | equivalent). Each will have the following data. |
| | | |
| | | ``subscriber`` |
| | | |
| | |
| | | ``predicates`` |
| | | |
| | | The predicate objects created as the result of passing predicate arguments |
| | | to ``add_subscriber`` |
| | | to ``add_subscriber``. |
| | | |
| | | ``derived_predicates`` |
| | | |
| | |
| | | |
| | | Each introspectable in the ``response adapters`` category represents a call |
| | | to :meth:`pyramid.config.Configurator.add_response_adapter` (or a decorator |
| | | equivalent); each will have the following data. |
| | | equivalent). Each will have the following data. |
| | | |
| | | ``adapter`` |
| | | |
| | |
| | | |
| | | ``type`` |
| | | |
| | | The resolved ``type_or_iface`` argument passed to |
| | | ``add_response_adapter``. |
| | | The resolved ``type_or_iface`` argument passed to ``add_response_adapter``. |
| | | |
| | | ``root factories`` |
| | | |
| | | Each introspectable in the ``root factories`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.set_root_factory` (or the Configurator |
| | | constructor equivalent) *or* a ``factory`` argument passed to |
| | | :meth:`pyramid.config.Configurator.add_route`; each will have the following |
| | | :meth:`pyramid.config.Configurator.add_route`. Each will have the following |
| | | data. |
| | | |
| | | ``factory`` |
| | |
| | | |
| | | Only one introspectable will exist in the ``session factory`` category. It |
| | | represents a call to :meth:`pyramid.config.Configurator.set_session_factory` |
| | | (or the Configurator constructor equivalent); it will have the following |
| | | (or the Configurator constructor equivalent). It will have the following |
| | | data. |
| | | |
| | | ``factory`` |
| | |
| | | |
| | | Only one introspectable will exist in the ``request factory`` category. It |
| | | represents a call to :meth:`pyramid.config.Configurator.set_request_factory` |
| | | (or the Configurator constructor equivalent); it will have the following |
| | | (or the Configurator constructor equivalent). It will have the following |
| | | data. |
| | | |
| | | ``factory`` |
| | |
| | | |
| | | ``locale negotiator`` |
| | | |
| | | Only one introspectable will exist in the ``locale negotiator`` category. |
| | | It represents a call to |
| | | Only one introspectable will exist in the ``locale negotiator`` category. It |
| | | represents a call to |
| | | :meth:`pyramid.config.Configurator.set_locale_negotiator` (or the |
| | | Configurator constructor equivalent); it will have the following data. |
| | | Configurator constructor equivalent). It will have the following data. |
| | | |
| | | ``negotiator`` |
| | | |
| | |
| | | |
| | | ``renderer factories`` |
| | | |
| | | Each introspectable in the ``renderer factories`` category represents a |
| | | call to :meth:`pyramid.config.Configurator.add_renderer` (or the |
| | | Configurator constructor equivalent); each will have the following data. |
| | | Each introspectable in the ``renderer factories`` category represents a call |
| | | to :meth:`pyramid.config.Configurator.add_renderer` (or the Configurator |
| | | constructor equivalent). Each will have the following data. |
| | | |
| | | ``name`` |
| | | |
| | |
| | | |
| | | ``factory`` |
| | | |
| | | The factory object (the resolved ``factory`` argument to |
| | | ``add_renderer``). |
| | | The factory object (the resolved ``factory`` argument to ``add_renderer``). |
| | | |
| | | ``routes`` |
| | | |
| | | Each introspectable in the ``routes`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.add_route`; each will have the following |
| | | :meth:`pyramid.config.Configurator.add_route`. Each will have the following |
| | | data. |
| | | |
| | | ``name`` |
| | |
| | | There will be one and only one introspectable in the ``authentication |
| | | policy`` category. It represents a call to the |
| | | :meth:`pyramid.config.Configurator.set_authentication_policy` method (or |
| | | its Configurator constructor equivalent); it will have the following data. |
| | | its Configurator constructor equivalent). It will have the following data. |
| | | |
| | | ``policy`` |
| | | |
| | |
| | | |
| | | ``authorization policy`` |
| | | |
| | | There will be one and only one introspectable in the ``authorization |
| | | policy`` category. It represents a call to the |
| | | There will be one and only one introspectable in the ``authorization policy`` |
| | | category. It represents a call to the |
| | | :meth:`pyramid.config.Configurator.set_authorization_policy` method (or its |
| | | Configurator constructor equivalent); it will have the following data. |
| | | Configurator constructor equivalent). It will have the following data. |
| | | |
| | | ``policy`` |
| | | |
| | |
| | | There will be one and only one introspectable in the ``default permission`` |
| | | category. It represents a call to the |
| | | :meth:`pyramid.config.Configurator.set_default_permission` method (or its |
| | | Configurator constructor equivalent); it will have the following data. |
| | | Configurator constructor equivalent). It will have the following data. |
| | | |
| | | ``value`` |
| | | |
| | |
| | | ``views`` |
| | | |
| | | Each introspectable in the ``views`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.add_view`; each will have the following |
| | | :meth:`pyramid.config.Configurator.add_view`. Each will have the following |
| | | data. |
| | | |
| | | ``name`` |
| | |
| | | |
| | | Each introspectable in the ``permissions`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.add_view` that has an explicit |
| | | ``permission`` argument to *or* a call to |
| | | :meth:`pyramid.config.Configurator.set_default_permission`; each will have |
| | | ``permission`` argument *or* a call to |
| | | :meth:`pyramid.config.Configurator.set_default_permission`. Each will have |
| | | the following data. |
| | | |
| | | ``value`` |
| | |
| | | |
| | | Each introspectable in the ``templates`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.add_view` that has a ``renderer`` |
| | | argument which points to a template; each will have the following data. |
| | | argument which points to a template. Each will have the following data. |
| | | |
| | | ``name`` |
| | | |
| | |
| | | |
| | | ``renderer`` |
| | | |
| | | The :class:`pyramid.interfaces.IRendererInfo` object which represents |
| | | this template's renderer. |
| | | The :class:`pyramid.interfaces.IRendererInfo` object which represents this |
| | | template's renderer. |
| | | |
| | | ``view mappers`` |
| | | |
| | | Each introspectable in the ``view mappers`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.add_view` that has an explicit |
| | | ``mapper`` argument to *or* a call to |
| | | :meth:`pyramid.config.Configurator.set_view_mapper`; each will have |
| | | :meth:`pyramid.config.Configurator.add_view` that has an explicit ``mapper`` |
| | | argument *or* a call to |
| | | :meth:`pyramid.config.Configurator.set_view_mapper`. Each will have |
| | | the following data. |
| | | |
| | | ``mapper`` |
| | |
| | | |
| | | ``asset overrides`` |
| | | |
| | | Each introspectable in the ``asset overrides`` category represents a call |
| | | to :meth:`pyramid.config.Configurator.override_asset`; each will have the |
| | | Each introspectable in the ``asset overrides`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.override_asset`. Each will have the |
| | | following data. |
| | | |
| | | ``to_override`` |
| | | |
| | | The ``to_override`` argument (an asset spec) passed to |
| | | ``override_asset``. |
| | | The ``to_override`` argument (an asset spec) passed to ``override_asset``. |
| | | |
| | | ``override_with`` |
| | | |
| | |
| | | |
| | | ``translation directories`` |
| | | |
| | | Each introspectable in the ``translation directories`` category represents |
| | | an individual element in a ``specs`` argument passed to |
| | | :meth:`pyramid.config.Configurator.add_translation_dirs`; each will have |
| | | the following data. |
| | | Each introspectable in the ``translation directories`` category represents an |
| | | individual element in a ``specs`` argument passed to |
| | | :meth:`pyramid.config.Configurator.add_translation_dirs`. Each will have the |
| | | following data. |
| | | |
| | | ``directory`` |
| | | |
| | |
| | | ``tweens`` |
| | | |
| | | Each introspectable in the ``tweens`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.add_tween`; each will have the following |
| | | :meth:`pyramid.config.Configurator.add_tween`. Each will have the following |
| | | data. |
| | | |
| | | ``name`` |
| | | |
| | | The dotted name to the tween factory as a string (passed as |
| | | the ``tween_factory`` argument to ``add_tween``). |
| | | The dotted name to the tween factory as a string (passed as the |
| | | ``tween_factory`` argument to ``add_tween``). |
| | | |
| | | ``factory`` |
| | | |
| | |
| | | ``static views`` |
| | | |
| | | Each introspectable in the ``static views`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.add_static_view`; each will have the |
| | | :meth:`pyramid.config.Configurator.add_static_view`. Each will have the |
| | | following data. |
| | | |
| | | ``name`` |
| | |
| | | ``traversers`` |
| | | |
| | | Each introspectable in the ``traversers`` category represents a call to |
| | | :meth:`pyramid.config.Configurator.add_traverser`; each will have the |
| | | :meth:`pyramid.config.Configurator.add_traverser`. Each will have the |
| | | following data. |
| | | |
| | | ``iface`` |
| | | |
| | | The (resolved) interface or class object that represents the return value |
| | | of a root factory that this traverser will be used for. |
| | | of a root factory for which this traverser will be used. |
| | | |
| | | ``adapter`` |
| | | |
| | |
| | | ``resource url adapters`` |
| | | |
| | | Each introspectable in the ``resource url adapters`` category represents a |
| | | call to :meth:`pyramid.config.Configurator.add_resource_url_adapter`; each |
| | | call to :meth:`pyramid.config.Configurator.add_resource_url_adapter`. Each |
| | | will have the following data. |
| | | |
| | | ``adapter`` |
| | |
| | | ``resource_iface`` |
| | | |
| | | The (resolved) interface or class object that represents the resource |
| | | interface that this url adapter is registered for. |
| | | interface for which this URL adapter is registered. |
| | | |
| | | ``request_iface`` |
| | | |
| | | The (resolved) interface or class object that represents the request |
| | | interface that this url adapter is registered for. |
| | | interface for which this URL adapter is registered. |
| | | |
| | | Introspection in the Toolbar |
| | | ---------------------------- |
| | | |
| | | The Pyramid debug toolbar (part of the ``pyramid_debugtoolbar`` package) |
| | | provides a canned view of all registered introspectables and their |
| | | relationships. It looks something like this: |
| | | relationships. It is currently under the "Global" tab in the main navigation, |
| | | and it looks something like this: |
| | | |
| | | .. image:: tb_introspector.png |
| | | |
| | |
| | | Much Ado About Traversal |
| | | ======================== |
| | | |
| | | (Or, why you should care about it) |
| | | (Or, why you should care about it.) |
| | | |
| | | .. note:: |
| | | |
| | | This chapter was adapted, with permission, from a blog post by `Rob |
| | | Miller <http://blog.nonsequitarian.org/>`_, originally published at |
| | | http://blog.nonsequitarian.org/2010/much-ado-about-traversal/ . |
| | | This chapter was adapted, with permission, from a blog post by `Rob Miller |
| | | <http://blog.nonsequitarian.org/>`_, originally published at |
| | | http://blog.nonsequitarian.org/2010/much-ado-about-traversal/. |
| | | |
| | | Traversal is an alternative to :term:`URL dispatch` which allows |
| | | :app:`Pyramid` applications to map URLs to code. |
| | | Traversal is an alternative to :term:`URL dispatch` which allows :app:`Pyramid` |
| | | applications to map URLs to code. |
| | | |
| | | .. note:: |
| | | |
| | | Ex-Zope users who are already familiar with traversal and view lookup |
| | | conceptually may want to skip directly to the :ref:`traversal_chapter` |
| | | chapter, which discusses technical details. This chapter is mostly aimed |
| | | at people who have previous :term:`Pylons` experience or experience in |
| | | another framework which does not provide traversal, and need an |
| | | introduction to the "why" of traversal. |
| | | chapter, which discusses technical details. This chapter is mostly aimed at |
| | | people who have previous :term:`Pylons` experience or experience in another |
| | | framework which does not provide traversal, and need an introduction to the |
| | | "why" of traversal. |
| | | |
| | | Some folks who have been using Pylons and its Routes-based URL matching for a |
| | | long time are being exposed for the first time, via :app:`Pyramid`, to new |
| | | ideas such as ":term:`traversal`" and ":term:`view lookup`" as a way to route |
| | | incoming HTTP requests to callable code. Some of the same folks believe that |
| | | traversal is hard to understand. Others question its usefulness; URL |
| | | matching has worked for them so far, why should they even consider dealing |
| | | with another approach, one which doesn't fit their brain and which doesn't |
| | | provide any immediately obvious value? |
| | | traversal is hard to understand. Others question its usefulness; URL matching |
| | | has worked for them so far, so why should they even consider dealing with |
| | | another approach, one which doesn't fit their brain and which doesn't provide |
| | | any immediately obvious value? |
| | | |
| | | You can be assured that if you don't want to understand traversal, you don't |
| | | have to. You can happily build :app:`Pyramid` applications with only |
| | | :term:`URL dispatch`. However, there are some straightforward, real-world |
| | | use cases that are much more easily served by a traversal-based approach than |
| | | by a pattern-matching mechanism. Even if you haven't yet hit one of these |
| | | use cases yourself, understanding these new ideas is worth the effort for any |
| | | web developer so you know when you might want to use them. :term:`Traversal` |
| | | is actually a straightforward metaphor easily comprehended by anyone who's |
| | | ever used a run-of-the-mill file system with folders and files. |
| | | :term:`URL dispatch`. However, there are some straightforward, real-world use |
| | | cases that are much more easily served by a traversal-based approach than by a |
| | | pattern-matching mechanism. Even if you haven't yet hit one of these use cases |
| | | yourself, understanding these new ideas is worth the effort for any web |
| | | developer so you know when you might want to use them. :term:`Traversal` is |
| | | actually a straightforward metaphor easily comprehended by anyone who's ever |
| | | used a run-of-the-mill file system with folders and files. |
| | | |
| | | .. index:: |
| | | single: URL dispatch |
| | |
| | | URL Dispatch |
| | | ------------ |
| | | |
| | | Let's step back and consider the problem we're trying to solve. An |
| | | HTTP request for a particular path has been routed to our web |
| | | application. The requested path will possibly invoke a specific |
| | | :term:`view callable` function defined somewhere in our app. We're |
| | | trying to determine *which* callable function, if any, should be |
| | | invoked for a given requested URL. |
| | | Let's step back and consider the problem we're trying to solve. An HTTP |
| | | request for a particular path has been routed to our web application. The |
| | | requested path will possibly invoke a specific :term:`view callable` function |
| | | defined somewhere in our app. We're trying to determine *which* callable |
| | | function, if any, should be invoked for a given requested URL. |
| | | |
| | | Many systems, including Pyramid, offer a simple solution. They offer the |
| | | concept of "URL matching". URL matching approaches this problem by parsing |
| | | the URL path and comparing the results to a set of registered "patterns", |
| | | defined by a set of regular expressions, or some other URL path templating |
| | | syntax. Each pattern is mapped to a callable function somewhere; if the |
| | | request path matches a specific pattern, the associated function is called. |
| | | If the request path matches more than one pattern, some conflict resolution |
| | | scheme is used, usually a simple order precedence so that the first match |
| | | will take priority over any subsequent matches. If a request path doesn't |
| | | match any of the defined patterns, a "404 Not Found" response is returned. |
| | | concept of "URL matching". URL matching approaches this problem by parsing the |
| | | URL path and comparing the results to a set of registered "patterns", defined |
| | | by a set of regular expressions or some other URL path templating syntax. Each |
| | | pattern is mapped to a callable function somewhere; if the request path matches |
| | | a specific pattern, the associated function is called. If the request path |
| | | matches more than one pattern, some conflict resolution scheme is used, usually |
| | | a simple order precedence so that the first match will take priority over any |
| | | subsequent matches. If a request path doesn't match any of the defined |
| | | patterns, a "404 Not Found" response is returned. |
| | | |
| | | In Pyramid, we offer an implementation of URL matching which we call |
| | | :term:`URL dispatch`. Using :app:`Pyramid` syntax, we might have a match |
| | | pattern such as ``/{userid}/photos/{photoid}``, mapped to a ``photo_view()`` |
| | | function defined somewhere in our code. Then a request for a path such as |
| | | In Pyramid, we offer an implementation of URL matching which we call :term:`URL |
| | | dispatch`. Using :app:`Pyramid` syntax, we might have a match pattern such as |
| | | ``/{userid}/photos/{photoid}``, mapped to a ``photo_view()`` function defined |
| | | somewhere in our code. Then a request for a path such as |
| | | ``/joeschmoe/photos/photo1`` would be a match, and the ``photo_view()`` |
| | | function would be invoked to handle the request. Similarly, |
| | | ``/{userid}/blog/{year}/{month}/{postid}`` might map to a |
| | | ``blog_post_view()`` function, so ``/joeschmoe/blog/2010/12/urlmatching`` |
| | | would trigger the function, which presumably would know how to find and |
| | | render the ``urlmatching`` blog post. |
| | | ``/{userid}/blog/{year}/{month}/{postid}`` might map to a ``blog_post_view()`` |
| | | function, so ``/joeschmoe/blog/2010/12/urlmatching`` would trigger the |
| | | function, which presumably would know how to find and render the |
| | | ``urlmatching`` blog post. |
| | | |
| | | Historical Refresher |
| | | -------------------- |
| | |
| | | :app:`Pyramid`. Instead, we had general purpose HTTP servers that primarily |
| | | served files off of a file system. The "root" of a given site mapped to a |
| | | particular folder somewhere on the file system. Each segment of the request |
| | | URL path represented a subdirectory. The final path segment would be either |
| | | a directory or a file, and once the server found the right file it would |
| | | package it up in an HTTP response and send it back to the client. So serving |
| | | up a request for ``/joeschmoe/photos/photo1`` literally meant that there was |
| | | a ``joeschmoe`` folder somewhere, which contained a ``photos`` folder, which |
| | | in turn contained a ``photo1`` file. If at any point along the way we find |
| | | that there is not a folder or file matching the requested path, we return a |
| | | 404 response. |
| | | URL path represented a subdirectory. The final path segment would be either a |
| | | directory or a file, and once the server found the right file it would package |
| | | it up in an HTTP response and send it back to the client. So serving up a |
| | | request for ``/joeschmoe/photos/photo1`` literally meant that there was a |
| | | ``joeschmoe`` folder somewhere, which contained a ``photos`` folder, which in |
| | | turn contained a ``photo1`` file. If at any point along the way we find that |
| | | there is not a folder or file matching the requested path, we return a 404 |
| | | response. |
| | | |
| | | As the web grew more dynamic, however, a little bit of extra complexity was |
| | | added. Technologies such as CGI and HTTP server modules were developed. |
| | | Files were still looked up on the file system, but if the file ended with |
| | | (for example) ``.cgi`` or ``.php``, or if it lived in a special folder, |
| | | instead of simply sending the file to the client the server would read the |
| | | file, execute it using an interpreter of some sort, and then send the output |
| | | from this process to the client as the final result. The server |
| | | configuration specified which files would trigger some dynamic code, with the |
| | | default case being to just serve the static file. |
| | | added. Technologies such as CGI and HTTP server modules were developed. Files |
| | | were still looked up on the file system, but if the file ended with (for |
| | | example) ``.cgi`` or ``.php``, or if it lived in a special folder, instead of |
| | | simply sending the file to the client the server would read the file, execute |
| | | it using an interpreter of some sort, and then send the output from this |
| | | process to the client as the final result. The server configuration specified |
| | | which files would trigger some dynamic code, with the default case being to |
| | | just serve the static file. |
| | | |
| | | .. index:: |
| | | single: traversal |
| | | |
| | | Traversal (aka Resource Location) |
| | | --------------------------------- |
| | | Traversal (a.k.a., Resource Location) |
| | | ------------------------------------- |
| | | |
| | | Believe it or not, if you understand how serving files from a file system |
| | | works, you understand traversal. And if you understand that a server might do |
| | | something different based on what type of file a given request specifies, |
| | | then you understand view lookup. |
| | | something different based on what type of file a given request specifies, then |
| | | you understand view lookup. |
| | | |
| | | The major difference between file system lookup and traversal is that a file |
| | | system lookup steps through nested directories and files in a file system |
| | | tree, while traversal steps through nested dictionary-type objects in a |
| | | :term:`resource tree`. Let's take a detailed look at one of our example |
| | | paths, so we can see what I mean: |
| | | system lookup steps through nested directories and files in a file system tree, |
| | | while traversal steps through nested dictionary-type objects in a |
| | | :term:`resource tree`. Let's take a detailed look at one of our example paths, |
| | | so we can see what I mean. |
| | | |
| | | The path ``/joeschmoe/photos/photo1``, has four segments: ``/``, |
| | | ``joeschmoe``, ``photos`` and ``photo1``. With file system lookup we might |
| | | have a root folder (``/``) containing a nested folder (``joeschmoe``), which |
| | | contains another nested folder (``photos``), which finally contains a JPG |
| | | file (``photo1``). With traversal, we instead have a dictionary-like root |
| | | object. Asking for the ``joeschmoe`` key gives us another dictionary-like |
| | | object. Asking this in turn for the ``photos`` key gives us yet another |
| | | mapping object, which finally (hopefully) contains the resource that we're |
| | | looking for within its values, referenced by the ``photo1`` key. |
| | | The path ``/joeschmoe/photos/photo1``, has four segments: ``/``, ``joeschmoe``, |
| | | ``photos`` and ``photo1``. With file system lookup we might have a root folder |
| | | (``/``) containing a nested folder (``joeschmoe``), which contains another |
| | | nested folder (``photos``), which finally contains a JPG file (``photo1``). |
| | | With traversal, we instead have a dictionary-like root object. Asking for the |
| | | ``joeschmoe`` key gives us another dictionary-like object. Asking in turn for |
| | | the ``photos`` key gives us yet another mapping object, which finally |
| | | (hopefully) contains the resource that we're looking for within its values, |
| | | referenced by the ``photo1`` key. |
| | | |
| | | In pure Python terms, then, the traversal or "resource location" |
| | | portion of satisfying the ``/joeschmoe/photos/photo1`` request |
| | | will look something like this pseudocode:: |
| | | In pure Python terms, then, the traversal or "resource location" portion of |
| | | satisfying the ``/joeschmoe/photos/photo1`` request will look something like |
| | | this pseudocode:: |
| | | |
| | | get_root()['joeschmoe']['photos']['photo1'] |
| | | |
| | | ``get_root()`` is some function that returns a root traversal |
| | | :term:`resource`. If all of the specified keys exist, then the returned |
| | | object will be the resource that is being requested, analogous to the JPG |
| | | file that was retrieved in the file system example. If a :exc:`KeyError` is |
| | | generated anywhere along the way, :app:`Pyramid` will return 404. (This |
| | | isn't precisely true, as you'll see when we learn about view lookup below, |
| | | but the basic idea holds.) |
| | | ``get_root()`` is some function that returns a root traversal :term:`resource`. |
| | | If all of the specified keys exist, then the returned object will be the |
| | | resource that is being requested, analogous to the JPG file that was retrieved |
| | | in the file system example. If a :exc:`KeyError` is generated anywhere along |
| | | the way, :app:`Pyramid` will return 404. (This isn't precisely true, as you'll |
| | | see when we learn about view lookup below, but the basic idea holds.) |
| | | |
| | | .. index:: |
| | | single: resource |
| | |
| | | What *are* they?" |
| | | |
| | | Since :app:`Pyramid` is not a highly opinionated framework, it makes no |
| | | restriction on how a :term:`resource` is implemented; a developer can |
| | | implement them as he wishes. One common pattern used is to persist all of |
| | | the resources, including the root, in a database as a graph. The root object |
| | | is a dictionary-like object. Dictionary-like objects in Python supply a |
| | | restriction on how a :term:`resource` is implemented; a developer can implement |
| | | them as they wish. One common pattern used is to persist all of the resources, |
| | | including the root, in a database as a graph. The root object is a |
| | | dictionary-like object. Dictionary-like objects in Python supply a |
| | | ``__getitem__`` method which is called when key lookup is done. Under the |
| | | hood, when ``adict`` is a dictionary-like object, Python translates |
| | | ``adict['a']`` to ``adict.__getitem__('a')``. Try doing this in a Python |
| | |
| | | >>> adict.__getitem__('a') |
| | | 1 |
| | | |
| | | |
| | | The dictionary-like root object stores the ids of all of its subresources as |
| | | keys, and provides a ``__getitem__`` implementation that fetches them. So |
| | | ``get_root()`` fetches the unique root object, while |
| | | ``get_root()['joeschmoe']`` returns a different object, also stored in the |
| | | database, which in turn has its own subresources and ``__getitem__`` |
| | | implementation, etc. These resources might be persisted in a relational |
| | | implementation, and so on. These resources might be persisted in a relational |
| | | database, one of the many "NoSQL" solutions that are becoming popular these |
| | | days, or anywhere else, it doesn't matter. As long as the returned objects |
| | | provide the dictionary-like API (i.e. as long as they have an appropriately |
| | | implemented ``__getitem__`` method) then traversal will work. |
| | | days, or anywhere else; it doesn't matter. As long as the returned objects |
| | | provide the dictionary-like API (i.e., as long as they have an appropriately |
| | | implemented ``__getitem__`` method), then traversal will work. |
| | | |
| | | In fact, you don't need a "database" at all. You could use plain |
| | | dictionaries, with your site's URL structure hard-coded directly in |
| | | the Python source. Or you could trivially implement a set of objects |
| | | with ``__getitem__`` methods that search for files in specific |
| | | directories, and thus precisely recreate the traditional mechanism of |
| | | having the URL path mapped directly to a folder structure on the file |
| | | system. Traversal is in fact a superset of file system lookup. |
| | | In fact, you don't need a "database" at all. You could use plain dictionaries, |
| | | with your site's URL structure hard-coded directly in the Python source. Or |
| | | you could trivially implement a set of objects with ``__getitem__`` methods |
| | | that search for files in specific directories, and thus precisely recreate the |
| | | traditional mechanism of having the URL path mapped directly to a folder |
| | | structure on the file system. Traversal is in fact a superset of file system |
| | | lookup. |
| | | |
| | | .. note:: See the chapter entitled :ref:`resources_chapter` for a more |
| | | technical overview of resources. |
| | |
| | | process by which a specific resource is retrieved according to a specific URL |
| | | path. But what is "view lookup"? |
| | | |
| | | The need for view lookup is simple: there is more than one possible action |
| | | that you might want to take after finding a :term:`resource`. With our photo |
| | | The need for view lookup is simple: there is more than one possible action that |
| | | you might want to take after finding a :term:`resource`. With our photo |
| | | example, for instance, you might want to view the photo in a page, but you |
| | | might also want to provide a way for the user to edit the photo and any |
| | | associated metadata. We'll call the former the ``view`` view, and the latter |
| | | will be the ``edit`` view. (Original, I know.) :app:`Pyramid` has a |
| | | centralized view :term:`application registry` where named views can be |
| | | associated with specific resource types. So in our example, we'll assume |
| | | that we've registered ``view`` and ``edit`` views for photo objects, and that |
| | | we've specified the ``view`` view as the default, so that |
| | | associated with specific resource types. So in our example, we'll assume that |
| | | we've registered ``view`` and ``edit`` views for photo objects, and that we've |
| | | specified the ``view`` view as the default, so that |
| | | ``/joeschmoe/photos/photo1/view`` and ``/joeschmoe/photos/photo1`` are |
| | | equivalent. The edit view would sensibly be provided by a request for |
| | | ``/joeschmoe/photos/photo1/edit``. |
| | | |
| | | Hopefully it's clear that the first portion of the edit view's URL path is |
| | | going to resolve to the same resource as the non-edit version, specifically |
| | | the resource returned by ``get_root()['joeschmoe']['photos']['photo1']``. |
| | | But traveral ends there; the ``photo1`` resource doesn't have an ``edit`` |
| | | key. In fact, it might not even be a dictionary-like object, in which case |
| | | going to resolve to the same resource as the non-edit version, specifically the |
| | | resource returned by ``get_root()['joeschmoe']['photos']['photo1']``. But |
| | | traversal ends there; the ``photo1`` resource doesn't have an ``edit`` key. In |
| | | fact, it might not even be a dictionary-like object, in which case |
| | | ``photo1['edit']`` would be meaningless. When the :app:`Pyramid` resource |
| | | location has been resolved to a *leaf* resource, but the entire request path |
| | | has not yet been expended, the *very next* path segment is treated as a |
| | | :term:`view name`. The registry is then checked to see if a view of the |
| | | given name has been specified for a resource of the given type. If so, the |
| | | view callable is invoked, with the resource passed in as the related |
| | | ``context`` object (also available as ``request.context``). If a view |
| | | callable could not be found, :app:`Pyramid` will return a "404 Not Found" |
| | | response. |
| | | :term:`view name`. The registry is then checked to see if a view of the given |
| | | name has been specified for a resource of the given type. If so, the view |
| | | callable is invoked, with the resource passed in as the related ``context`` |
| | | object (also available as ``request.context``). If a view callable could not |
| | | be found, :app:`Pyramid` will return a "404 Not Found" response. |
| | | |
| | | You might conceptualize a request for ``/joeschmoe/photos/photo1/edit`` as |
| | | ultimately converted into the following piece of Pythonic pseudocode:: |
| | |
| | | view_callable(request) |
| | | |
| | | The ``get_root`` and ``get_view`` functions don't really exist. Internally, |
| | | :app:`Pyramid` does something more complicated. But the example above |
| | | is a reasonable approximation of the view lookup algorithm in pseudocode. |
| | | :app:`Pyramid` does something more complicated. But the example above is a |
| | | reasonable approximation of the view lookup algorithm in pseudocode. |
| | | |
| | | Use Cases |
| | | --------- |
| | |
| | | |
| | | /{userid}/{typename}/{objectid}[/{view_name}] |
| | | |
| | | In all of the examples thus far, we've hard coded the typename value, |
| | | assuming that we'd know at development time what names were going to be used |
| | | ("photos", "blog", etc.). But what if we don't know what these names will |
| | | be? Or, worse yet, what if we don't know *anything* about the structure of |
| | | the URLs inside a user's folder? We could be writing a CMS where we want the |
| | | end user to be able to arbitrarily add content and other folders inside his |
| | | folder. He might decide to nest folders dozens of layers deep. How will you |
| | | construct matching patterns that could account for every possible combination |
| | | of paths that might develop? |
| | | In all of the examples thus far, we've hard coded the typename value, assuming |
| | | that we'd know at development time what names were going to be used ("photos", |
| | | "blog", etc.). But what if we don't know what these names will be? Or, worse |
| | | yet, what if we don't know *anything* about the structure of the URLs inside a |
| | | user's folder? We could be writing a CMS where we want the end user to be able |
| | | to arbitrarily add content and other folders inside his folder. He might |
| | | decide to nest folders dozens of layers deep. How will you construct matching |
| | | patterns that could account for every possible combination of paths that might |
| | | develop? |
| | | |
| | | It might be possible, but it certainly won't be easy. The matching |
| | | patterns are going to become complex quickly as you try to handle all |
| | | of the edge cases. |
| | | It might be possible, but it certainly won't be easy. The matching patterns |
| | | are going to become complex quickly as you try to handle all of the edge cases. |
| | | |
| | | With traversal, however, it's straightforward. Twenty layers of nesting |
| | | would be no problem. :app:`Pyramid` will happily call ``__getitem__`` as |
| | | many times as it needs to, until it runs out of path segments or until a |
| | | resource raises a :exc:`KeyError`. Each resource only needs to know how to |
| | | fetch its immediate children, the traversal algorithm takes care of the rest. |
| | | Also, since the structure of the resource tree can live in the database and |
| | | not in the code, it's simple to let users modify the tree at runtime to set |
| | | up their own personalized "directory" structures. |
| | | With traversal, however, it's straightforward. Twenty layers of nesting would |
| | | be no problem. :app:`Pyramid` will happily call ``__getitem__`` as many times |
| | | as it needs to, until it runs out of path segments or until a resource raises a |
| | | :exc:`KeyError`. Each resource only needs to know how to fetch its immediate |
| | | children, and the traversal algorithm takes care of the rest. Also, since the |
| | | structure of the resource tree can live in the database and not in the code, |
| | | it's simple to let users modify the tree at runtime to set up their own |
| | | personalized "directory" structures. |
| | | |
| | | Another use case in which traversal shines is when there is a need to support |
| | | a context-dependent security policy. One example might be a document |
| | | management infrastructure for a large corporation, where members of different |
| | | departments have varying access levels to the various other departments' |
| | | files. Reasonably, even specific files might need to be made available to |
| | | specific individuals. Traversal does well here if your resources actually |
| | | represent the data objects related to your documents, because the idea of a |
| | | resource authorization is baked right into the code resolution and calling |
| | | process. Resource objects can store ACLs, which can be inherited and/or |
| | | overridden by the subresources. |
| | | Another use case in which traversal shines is when there is a need to support a |
| | | context-dependent security policy. One example might be a document management |
| | | infrastructure for a large corporation, where members of different departments |
| | | have varying access levels to the various other departments' files. |
| | | Reasonably, even specific files might need to be made available to specific |
| | | individuals. Traversal does well here if your resources actually represent the |
| | | data objects related to your documents, because the idea of a resource |
| | | authorization is baked right into the code resolution and calling process. |
| | | Resource objects can store ACLs, which can be inherited and/or overridden by |
| | | the subresources. |
| | | |
| | | If each resource can thus generate a context-based ACL, then whenever view |
| | | code is attempting to perform a sensitive action, it can check against that |
| | | ACL to see whether the current user should be allowed to perform the action. |
| | | In this way you achieve so called "instance based" or "row level" security |
| | | which is considerably harder to model using a traditional tabular approach. |
| | | If each resource can thus generate a context-based ACL, then whenever view code |
| | | is attempting to perform a sensitive action, it can check against that ACL to |
| | | see whether the current user should be allowed to perform the action. In this |
| | | way you achieve so called "instance based" or "row level" security which is |
| | | considerably harder to model using a traditional tabular approach. |
| | | :app:`Pyramid` actively supports such a scheme, and in fact if you register |
| | | your views with guard permissions and use an authorization policy, |
| | | :app:`Pyramid` can check against a resource's ACL when deciding whether or |
| | | not the view itself is available to the current user. |
| | | your views with guarded permissions and use an authorization policy, |
| | | :app:`Pyramid` can check against a resource's ACL when deciding whether or not |
| | | the view itself is available to the current user. |
| | | |
| | | In summary, there are entire classes of problems that are more easily served |
| | | by traversal and view lookup than by :term:`URL dispatch`. If your problems |
| | | don't require it, great: stick with :term:`URL dispatch`. But if you're |
| | | using :app:`Pyramid` and you ever find that you *do* need to support one of |
| | | these use cases, you'll be glad you have traversal in your toolkit. |
| | | In summary, there are entire classes of problems that are more easily served by |
| | | traversal and view lookup than by :term:`URL dispatch`. If your problems don't |
| | | require it, great, stick with :term:`URL dispatch`. But if you're using |
| | | :app:`Pyramid` and you ever find that you *do* need to support one of these use |
| | | cases, you'll be glad you have traversal in your toolkit. |
| | | |
| | | .. note:: |
| | | |
| | | It is even possible to mix and match :term:`traversal` with |
| | | :term:`URL dispatch` in the same :app:`Pyramid` application. See the |
| | | It is even possible to mix and match :term:`traversal` with :term:`URL |
| | | dispatch` in the same :app:`Pyramid` application. See the |
| | | :ref:`hybrid_chapter` chapter for details. |
| | |
| | | ``alchemy`` |
| | | URL mapping via :term:`URL dispatch` and persistence via :term:`SQLAlchemy` |
| | | |
| | | |
| | | .. index:: |
| | | single: creating a project |
| | | single: project |
| | | single: pcreate |
| | | |
| | | .. _creating_a_project: |
| | | |
| | | Creating the Project |
| | | -------------------- |
| | | |
| | | .. seealso:: See also the output of :ref:`pcreate --help <pcreate_script>`. |
| | | |
| | | In :ref:`installing_chapter`, you created a virtual Python environment via the |
| | | ``virtualenv`` command. To start a :app:`Pyramid` :term:`project`, use the |
| | |
| | | |
| | | On UNIX: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/pcreate -s starter MyProject |
| | | |
| | |
| | | Here's sample output from a run of ``pcreate`` on UNIX for a project we name |
| | | ``MyProject``: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/pcreate -s starter MyProject |
| | | Creating template pyramid |
| | |
| | | |
| | | On UNIX: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ cd MyProject |
| | | $ $VENV/bin/python setup.py develop |
| | |
| | | |
| | | Elided output from a run of this command on UNIX is shown below: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ cd MyProject |
| | | $ $VENV/bin/python setup.py develop |
| | |
| | | |
| | | On UNIX: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/python setup.py test -q |
| | | |
| | |
| | | |
| | | Here's sample output from a test run on UNIX: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/python setup.py test -q |
| | | running test |
| | |
| | | writing dependency_links to MyProject.egg-info/dependency_links.txt |
| | | writing entry points to MyProject.egg-info/entry_points.txt |
| | | reading manifest file 'MyProject.egg-info/SOURCES.txt' |
| | | reading manifest template 'MANIFEST.in' |
| | | warning: no files found matching '*.cfg' |
| | | warning: no files found matching '*.rst' |
| | | warning: no files found matching '*.ico' under directory 'myproject' |
| | | warning: no files found matching '*.gif' under directory 'myproject' |
| | | warning: no files found matching '*.jpg' under directory 'myproject' |
| | | warning: no files found matching '*.txt' under directory 'myproject' |
| | | warning: no files found matching '*.mak' under directory 'myproject' |
| | | warning: no files found matching '*.mako' under directory 'myproject' |
| | | warning: no files found matching '*.js' under directory 'myproject' |
| | | warning: no files found matching '*.html' under directory 'myproject' |
| | | warning: no files found matching '*.xml' under directory 'myproject' |
| | | writing manifest file 'MyProject.egg-info/SOURCES.txt' |
| | | running build_ext |
| | | .. |
| | | . |
| | | ---------------------------------------------------------------------- |
| | | Ran 1 test in 0.108s |
| | | Ran 1 test in 0.008s |
| | | |
| | | OK |
| | | |
| | |
| | | Running the Project Application |
| | | ------------------------------- |
| | | |
| | | .. seealso:: See also the output of :ref:`pserve --help <pserve_script>`. |
| | | |
| | | Once a project is installed for development, you can run the application it |
| | | represents using the ``pserve`` command against the generated configuration |
| | | file. In our case, this file is named ``development.ini``. |
| | | |
| | | On UNIX: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/pserve development.ini |
| | | |
| | |
| | | |
| | | Here's sample output from a run of ``pserve`` on UNIX: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/pserve development.ini |
| | | Starting server in PID 16601. |
| | | serving on http://0.0.0.0:6543 |
| | | Starting server in PID 16208. |
| | | serving on http://127.0.0.1:6543 |
| | | |
| | | When you use ``pserve`` to start the application implied by the default |
| | | rendering of a scaffold, it will respond to requests on *all* IP addresses |
| | | possessed by your system, not just requests to ``localhost``. This is what the |
| | | ``0.0.0.0`` in ``serving on http://0.0.0.0:6543`` means. The server will |
| | | respond to requests made to ``127.0.0.1`` and on any external IP address. For |
| | | example, your system might be configured to have an external IP address |
| | | ``192.168.1.50``. If that's the case, if you use a browser running on the same |
| | | system as Pyramid, it will be able to access the application via |
| | | ``http://127.0.0.1:6543/`` as well as via ``http://192.168.1.50:6543/``. |
| | | However, *other people* on other computers on the same network will also be |
| | | able to visit your Pyramid application in their browser by visiting |
| | | ``http://192.168.1.50:6543/``. |
| | | |
| | | If you want to restrict access such that only a browser running on the same |
| | | machine as Pyramid will be able to access your Pyramid application, edit the |
| | | Access is restricted such that only a browser running on the same machine as |
| | | Pyramid will be able to access your Pyramid application. However, if you want |
| | | to open access to other machines on the same network, then edit the |
| | | ``development.ini`` file, and replace the ``host`` value in the |
| | | ``[server:main]`` section. Change it from ``0.0.0.0`` to ``127.0.0.1``. For |
| | | ``[server:main]`` section, changing it from ``127.0.0.1`` to ``0.0.0.0``. For |
| | | example: |
| | | |
| | | .. code-block:: ini |
| | | |
| | | [server:main] |
| | | use = egg:waitress#main |
| | | host = 127.0.0.1 |
| | | host = 0.0.0.0 |
| | | port = 6543 |
| | | |
| | | Now when you use ``pserve`` to start the application, it will respond to |
| | | requests on *all* IP addresses possessed by your system, not just requests to |
| | | ``localhost``. This is what the ``0.0.0.0`` in |
| | | ``serving on http://0.0.0.0:6543`` means. The server will respond to requests |
| | | made to ``127.0.0.1`` and on any external IP address. For example, your system |
| | | might be configured to have an external IP address ``192.168.1.50``. If that's |
| | | the case, if you use a browser running on the same system as Pyramid, it will |
| | | be able to access the application via ``http://127.0.0.1:6543/`` as well as via |
| | | ``http://192.168.1.50:6543/``. However, *other people* on other computers on |
| | | the same network will also be able to visit your Pyramid application in their |
| | | browser by visiting ``http://192.168.1.50:6543/``. |
| | | |
| | | You can change the port on which the server runs on by changing the same |
| | | portion of the ``development.ini`` file. For example, you can change the |
| | |
| | | $ $VENV/bin/pserve development.ini --reload |
| | | Starting subprocess with file monitor |
| | | Starting server in PID 16601. |
| | | serving on http://0.0.0.0:6543 |
| | | serving on http://127.0.0.1:6543 |
| | | |
| | | Now if you make a change to any of your project's ``.py`` files or ``.ini`` |
| | | files, you'll see the server restart automatically: |
| | |
| | | development.ini changed; reloading... |
| | | -------------------- Restarting -------------------- |
| | | Starting server in PID 16602. |
| | | serving on http://0.0.0.0:6543 |
| | | serving on http://127.0.0.1:6543 |
| | | |
| | | Changes to template files (such as ``.pt`` or ``.mak`` files) won't cause the |
| | | server to restart. Changes to template files don't require a server restart as |
| | |
| | | is the default application. |
| | | |
| | | The ``[server:main]`` section of the configuration file configures a WSGI |
| | | server which listens on TCP port 6543. It is configured to listen on all |
| | | interfaces (``0.0.0.0``). This means that any remote system which has TCP |
| | | access to your system can see your Pyramid application. |
| | | server which listens on TCP port 6543. It is configured to listen on localhost |
| | | only (``127.0.0.1``). |
| | | |
| | | .. _MyProject_ini_logging: |
| | | |
| | | The sections that live between the markers ``# Begin logging configuration`` |
| | | and ``# End logging configuration`` represent Python's standard library |
| | | :mod:`logging` module configuration for your application. The sections between |
| | | these two markers are passed to the `logging module's config file configuration |
| | | engine <http://docs.python.org/howto/logging.html#configuring-logging>`_ when |
| | | the ``pserve`` or ``pshell`` commands are executed. The default configuration |
| | | The sections after ``# logging configuration`` represent Python's standard |
| | | library :mod:`logging` module configuration for your application. These |
| | | sections are passed to the `logging module's config file configuration engine |
| | | <http://docs.python.org/howto/logging.html#configuring-logging>`_ when the |
| | | ``pserve`` or ``pshell`` commands are executed. The default configuration |
| | | sends application logging output to the standard error output of your terminal. |
| | | For more information about logging configuration, see :ref:`logging_chapter`. |
| | | |
| | |
| | | ``setup.py`` is the de facto standard which Python developers use to |
| | | distribute their reusable code. You can read more about ``setup.py`` files |
| | | and their usage in the `Setuptools documentation |
| | | <http://peak.telecommunity.com/DevCenter/setuptools>`_ and `The Hitchhiker's |
| | | Guide to Packaging <http://guide.python-distribute.org/>`_. |
| | | <http://peak.telecommunity.com/DevCenter/setuptools>`_ and `Python Packaging |
| | | User Guide <https://packaging.python.org/en/latest/>`_. |
| | | |
| | | Our generated ``setup.py`` looks like this: |
| | | |
| | |
| | | |
| | | .. literalinclude:: MyProject/myproject/tests.py |
| | | :language: python |
| | | :lines: 1-18 |
| | | :lines: 1-17 |
| | | :linenos: |
| | | |
| | | This sample ``tests.py`` file has a single unit test defined within it. This |
| | |
| | | :ref:`Conflict Resolution <automatic_conflict_resolution>` mechanism. This |
| | | means that, in most cases, overriding a renderer is as simple as using the |
| | | :meth:`pyramid.config.Configurator.add_renderer` method to redefine the |
| | | template extension. For example, if you would like to override the ``.txt`` |
| | | extension to specify a new renderer, you could do the following: |
| | | template extension. For example, if you would like to override the ``json`` |
| | | renderer to specify a new renderer, you could do the following: |
| | | |
| | | .. code-block:: python |
| | | |
| | |
| | | Resources |
| | | ========= |
| | | |
| | | A :term:`resource` is an object that represents a "place" in a tree |
| | | related to your application. Every :app:`Pyramid` application has at |
| | | least one resource object: the :term:`root` resource. Even if you don't |
| | | define a root resource manually, a default one is created for you. The |
| | | root resource is the root of a :term:`resource tree`. A resource tree |
| | | is a set of nested dictionary-like objects which you can use to |
| | | represent your website's structure. |
| | | A :term:`resource` is an object that represents a "place" in a tree related to |
| | | your application. Every :app:`Pyramid` application has at least one resource |
| | | object: the :term:`root` resource. Even if you don't define a root resource |
| | | manually, a default one is created for you. The root resource is the root of a |
| | | :term:`resource tree`. A resource tree is a set of nested dictionary-like |
| | | objects which you can use to represent your website's structure. |
| | | |
| | | In an application which uses :term:`traversal` to map URLs to code, the |
| | | resource tree structure is used heavily to map each URL to a :term:`view |
| | | callable`. When :term:`traversal` is used, :app:`Pyramid` will walk |
| | | through the resource tree by traversing through its nested dictionary |
| | | structure in order to find a :term:`context` resource. Once a context |
| | | resource is found, the context resource and data in the request will be |
| | | used to find a :term:`view callable`. |
| | | callable`. When :term:`traversal` is used, :app:`Pyramid` will walk through |
| | | the resource tree by traversing through its nested dictionary structure in |
| | | order to find a :term:`context` resource. Once a context resource is found, |
| | | the context resource and data in the request will be used to find a :term:`view |
| | | callable`. |
| | | |
| | | In an application which uses :term:`URL dispatch`, the resource tree is only |
| | | used indirectly, and is often "invisible" to the developer. In URL dispatch |
| | | applications, the resource "tree" is often composed of only the root resource |
| | | by itself. This root resource sometimes has security declarations attached |
| | | to it, but is not required to have any. In general, the resource tree is |
| | | much less important in applications that use URL dispatch than applications |
| | | that use traversal. |
| | | by itself. This root resource sometimes has security declarations attached to |
| | | it, but is not required to have any. In general, the resource tree is much |
| | | less important in applications that use URL dispatch than applications that use |
| | | traversal. |
| | | |
| | | In "Zope-like" :app:`Pyramid` applications, resource objects also often store |
| | | data persistently, and offer methods related to mutating that persistent data. |
| | | In these kinds of applications, resources not only represent the site |
| | | structure of your website, but they become the :term:`domain model` of the |
| | | application. |
| | | In these kinds of applications, resources not only represent the site structure |
| | | of your website, but they become the :term:`domain model` of the application. |
| | | |
| | | Also: |
| | | |
| | | - The ``context`` and ``containment`` predicate arguments to |
| | | :meth:`~pyramid.config.Configurator.add_view` (or a |
| | | :func:`~pyramid.view.view_config` decorator) reference a resource class |
| | | or resource :term:`interface`. |
| | | :func:`~pyramid.view.view_config` decorator) reference a resource class or |
| | | resource :term:`interface`. |
| | | |
| | | - A :term:`root factory` returns a resource. |
| | | |
| | | - A resource is exposed to :term:`view` code as the :term:`context` of a |
| | | view. |
| | | - A resource is exposed to :term:`view` code as the :term:`context` of a view. |
| | | |
| | | - Various helpful :app:`Pyramid` API methods expect a resource as an argument |
| | | (e.g. :meth:`~pyramid.request.Request.resource_url` and others). |
| | | (e.g., :meth:`~pyramid.request.Request.resource_url` and others). |
| | | |
| | | .. index:: |
| | | single: resource tree |
| | |
| | | Defining a Resource Tree |
| | | ------------------------ |
| | | |
| | | When :term:`traversal` is used (as opposed to a purely :term:`url dispatch` |
| | | When :term:`traversal` is used (as opposed to a purely :term:`URL dispatch` |
| | | based application), :app:`Pyramid` expects to be able to traverse a tree |
| | | composed of resources (the :term:`resource tree`). Traversal begins at a |
| | | root resource, and descends into the tree recursively, trying each resource's |
| | | composed of resources (the :term:`resource tree`). Traversal begins at a root |
| | | resource, and descends into the tree recursively, trying each resource's |
| | | ``__getitem__`` method to resolve a path segment to another resource object. |
| | | :app:`Pyramid` imposes the following policy on resource instances in the |
| | | tree: |
| | | :app:`Pyramid` imposes the following policy on resource instances in the tree: |
| | | |
| | | - A container resource (a resource which contains other resources) must |
| | | supply a ``__getitem__`` method which is willing to resolve a unicode name |
| | | to a sub-resource. If a sub-resource by a particular name does not exist |
| | | in a container resource, ``__getitem__`` method of the container resource |
| | | must raise a :exc:`KeyError`. If a sub-resource by that name *does* exist, |
| | | the container's ``__getitem__`` should return the sub-resource. |
| | | - A container resource (a resource which contains other resources) must supply |
| | | a ``__getitem__`` method which is willing to resolve a Unicode name to a |
| | | sub-resource. If a sub-resource by a particular name does not exist in a |
| | | container resource, the ``__getitem__`` method of the container resource must |
| | | raise a :exc:`KeyError`. If a sub-resource by that name *does* exist, the |
| | | container's ``__getitem__`` should return the sub-resource. |
| | | |
| | | - Leaf resources, which do not contain other resources, must not implement a |
| | | ``__getitem__``, or if they do, their ``__getitem__`` method must always |
| | | raise a :exc:`KeyError`. |
| | | |
| | | See :ref:`traversal_chapter` for more information about how traversal |
| | | works against resource instances. |
| | | See :ref:`traversal_chapter` for more information about how traversal works |
| | | against resource instances. |
| | | |
| | | Here's a sample resource tree, represented by a variable named ``root``: |
| | | |
| | |
| | | |
| | | root = Resource({'a':Resource({'b':Resource({'c':Resource()})})}) |
| | | |
| | | The resource tree we've created above is represented by a dictionary-like |
| | | root object which has a single child named ``'a'``. ``'a'`` has a single child |
| | | The resource tree we've created above is represented by a dictionary-like root |
| | | object which has a single child named ``'a'``. ``'a'`` has a single child |
| | | named ``'b'``, and ``'b'`` has a single child named ``'c'``, which has no |
| | | children. It is therefore possible to access the ``'c'`` leaf resource like so: |
| | | |
| | |
| | | |
| | | root['a']['b']['c'] |
| | | |
| | | If you returned the above ``root`` object from a :term:`root factory`, the |
| | | path ``/a/b/c`` would find the ``'c'`` object in the resource tree as the |
| | | result of :term:`traversal`. |
| | | If you returned the above ``root`` object from a :term:`root factory`, the path |
| | | ``/a/b/c`` would find the ``'c'`` object in the resource tree as the result of |
| | | :term:`traversal`. |
| | | |
| | | In this example, each of the resources in the tree is of the same class. |
| | | This is not a requirement. Resource elements in the tree can be of any type. |
| | | We used a single class to represent all resources in the tree for the sake of |
| | | In this example, each of the resources in the tree is of the same class. This |
| | | is not a requirement. Resource elements in the tree can be of any type. We |
| | | used a single class to represent all resources in the tree for the sake of |
| | | simplicity, but in a "real" app, the resources in the tree can be arbitrary. |
| | | |
| | | Although the example tree above can service a traversal, the resource |
| | | instances in the above example are not aware of :term:`location`, so their |
| | | utility in a "real" application is limited. To make best use of built-in |
| | | :app:`Pyramid` API facilities, your resources should be "location-aware". |
| | | The next section details how to make resources location-aware. |
| | | Although the example tree above can service a traversal, the resource instances |
| | | in the above example are not aware of :term:`location`, so their utility in a |
| | | "real" application is limited. To make best use of built-in :app:`Pyramid` API |
| | | facilities, your resources should be "location-aware". The next section details |
| | | how to make resources location-aware. |
| | | |
| | | .. index:: |
| | | pair: location-aware; resource |
| | |
| | | |
| | | In order for certain :app:`Pyramid` location, security, URL-generation, and |
| | | traversal APIs to work properly against the resources in a resource tree, all |
| | | resources in the tree must be :term:`location` -aware. This means they must |
| | | resources in the tree must be :term:`location`-aware. This means they must |
| | | have two attributes: ``__parent__`` and ``__name__``. |
| | | |
| | | The ``__parent__`` attribute of a location-aware resource should be a |
| | | reference to the resource's parent resource instance in the tree. The |
| | | ``__name__`` attribute should be the name with which a resource's parent |
| | | refers to the resource via ``__getitem__``. |
| | | The ``__parent__`` attribute of a location-aware resource should be a reference |
| | | to the resource's parent resource instance in the tree. The ``__name__`` |
| | | attribute should be the name with which a resource's parent refers to the |
| | | resource via ``__getitem__``. |
| | | |
| | | The ``__parent__`` of the root resource should be ``None`` and its |
| | | ``__name__`` should be the empty string. For instance: |
| | | The ``__parent__`` of the root resource should be ``None`` and its ``__name__`` |
| | | should be the empty string. For instance: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | __name__ = '' |
| | | __parent__ = None |
| | | |
| | | A resource returned from the root resource's ``__getitem__`` method should |
| | | have a ``__parent__`` attribute that is a reference to the root resource, and |
| | | its ``__name__`` attribute should match the name by which it is reachable via |
| | | the root resource's ``__getitem__``. A container resource within the root |
| | | resource should have a ``__getitem__`` that returns resources with a |
| | | ``__parent__`` attribute that points at the container, and these subobjects |
| | | should have a ``__name__`` attribute that matches the name by which they are |
| | | retrieved from the container via ``__getitem__``. This pattern continues |
| | | recursively "up" the tree from the root. |
| | | A resource returned from the root resource's ``__getitem__`` method should have |
| | | a ``__parent__`` attribute that is a reference to the root resource, and its |
| | | ``__name__`` attribute should match the name by which it is reachable via the |
| | | root resource's ``__getitem__``. A container resource within the root resource |
| | | should have a ``__getitem__`` that returns resources with a ``__parent__`` |
| | | attribute that points at the container, and these sub-objects should have a |
| | | ``__name__`` attribute that matches the name by which they are retrieved from |
| | | the container via ``__getitem__``. This pattern continues recursively "up" the |
| | | tree from the root. |
| | | |
| | | The ``__parent__`` attributes of each resource form a linked list that points |
| | | "downwards" toward the root. This is analogous to the `..` entry in |
| | | "downwards" toward the root. This is analogous to the ``..`` entry in |
| | | filesystem directories. If you follow the ``__parent__`` values from any |
| | | resource in the resource tree, you will eventually come to the root resource, |
| | | just like if you keep executing the ``cd ..`` filesystem command, eventually |
| | |
| | | |
| | | .. warning:: |
| | | |
| | | If your root resource has a ``__name__`` argument that is not |
| | | ``None`` or the empty string, URLs returned by the |
| | | :func:`~pyramid.request.Request.resource_url` function and paths generated |
| | | If your root resource has a ``__name__`` argument that is not ``None`` or |
| | | the empty string, URLs returned by the |
| | | :func:`~pyramid.request.Request.resource_url` function, and paths generated |
| | | by the :func:`~pyramid.traversal.resource_path` and |
| | | :func:`~pyramid.traversal.resource_path_tuple` APIs will be generated |
| | | :func:`~pyramid.traversal.resource_path_tuple` APIs, will be generated |
| | | improperly. The value of ``__name__`` will be prepended to every path and |
| | | URL generated (as opposed to a single leading slash or empty tuple |
| | | element). |
| | | URL generated (as opposed to a single leading slash or empty tuple element). |
| | | |
| | | .. sidebar:: For your convenience |
| | | |
| | | If you'd rather not manage the ``__name__`` and ``__parent__`` attributes |
| | | of your resources "by hand", an add-on package named |
| | | If you'd rather not manage the ``__name__`` and ``__parent__`` attributes of |
| | | your resources "by hand", an add-on package named |
| | | :mod:`pyramid_traversalwrapper` can help. |
| | | |
| | | In order to use this helper feature, you must first install the |
| | | :mod:`pyramid_traversalwrapper` package (available via PyPI), then register |
| | | its ``ModelGraphTraverser`` as the traversal policy, rather than the |
| | | default :app:`Pyramid` traverser. The package contains instructions for |
| | | doing so. |
| | | its ``ModelGraphTraverser`` as the traversal policy, rather than the default |
| | | :app:`Pyramid` traverser. The package contains instructions for doing so. |
| | | |
| | | Once :app:`Pyramid` is configured with this feature, you will no longer |
| | | need to manage the ``__parent__`` and ``__name__`` attributes on resource |
| | | objects "by hand". Instead, as necessary, during traversal :app:`Pyramid` |
| | | will wrap each resource (even the root resource) in a ``LocationProxy`` |
| | | which will dynamically assign a ``__name__`` and a ``__parent__`` to the |
| | | traversed resource (based on the last traversed resource and the name |
| | | supplied to ``__getitem__``). The root resource will have a ``__name__`` |
| | | attribute of ``None`` and a ``__parent__`` attribute of ``None``. |
| | | Once :app:`Pyramid` is configured with this feature, you will no longer need |
| | | to manage the ``__parent__`` and ``__name__`` attributes on resource objects |
| | | "by hand". Instead, as necessary during traversal, :app:`Pyramid` will wrap |
| | | each resource (even the root resource) in a ``LocationProxy``, which will |
| | | dynamically assign a ``__name__`` and a ``__parent__`` to the traversed |
| | | resource, based on the last traversed resource and the name supplied to |
| | | ``__getitem__``. The root resource will have a ``__name__`` attribute of |
| | | ``None`` and a ``__parent__`` attribute of ``None``. |
| | | |
| | | Applications which use tree-walking :app:`Pyramid` APIs require |
| | | location-aware resources. These APIs include (but are not limited to) |
| | | Applications which use tree-walking :app:`Pyramid` APIs require location-aware |
| | | resources. These APIs include (but are not limited to) |
| | | :meth:`~pyramid.request.Request.resource_url`, |
| | | :func:`~pyramid.traversal.find_resource`, |
| | | :func:`~pyramid.traversal.find_root`, |
| | | :func:`~pyramid.traversal.find_resource`, :func:`~pyramid.traversal.find_root`, |
| | | :func:`~pyramid.traversal.find_interface`, |
| | | :func:`~pyramid.traversal.resource_path`, |
| | | :func:`~pyramid.traversal.resource_path_tuple`, or |
| | | :func:`~pyramid.traversal.resource_path_tuple`, |
| | | :func:`~pyramid.traversal.traverse`, :func:`~pyramid.traversal.virtual_root`, |
| | | and (usually) :meth:`~pyramid.request.Request.has_permission` and |
| | | :func:`~pyramid.security.principals_allowed_by_permission`. |
| | |
| | | |
| | | .. _generating_the_url_of_a_resource: |
| | | |
| | | Generating The URL Of A Resource |
| | | Generating the URL of a Resource |
| | | -------------------------------- |
| | | |
| | | If your resources are :term:`location` aware, you can use the |
| | | If your resources are :term:`location`-aware, you can use the |
| | | :meth:`pyramid.request.Request.resource_url` API to generate a URL for the |
| | | resource. This URL will use the resource's position in the parent tree to |
| | | create a resource path, and it will prefix the path with the current |
| | | application URL to form a fully-qualified URL with the scheme, host, port, |
| | | and path. You can also pass extra arguments to |
| | | application URL to form a fully-qualified URL with the scheme, host, port, and |
| | | path. You can also pass extra arguments to |
| | | :meth:`~pyramid.request.Request.resource_url` to influence the generated URL. |
| | | |
| | | The simplest call to :meth:`~pyramid.request.Request.resource_url` looks like |
| | |
| | | :term:`request` object. |
| | | |
| | | If the resource referred to as ``resource`` in the above example was the root |
| | | resource, and the host that was used to contact the server was |
| | | ``example.com``, the URL generated would be ``http://example.com/``. |
| | | However, if the resource was a child of the root resource named ``a``, the |
| | | generated URL would be ``http://example.com/a/``. |
| | | resource, and the host that was used to contact the server was ``example.com``, |
| | | the URL generated would be ``http://example.com/``. However, if the resource |
| | | was a child of the root resource named ``a``, the generated URL would be |
| | | ``http://example.com/a/``. |
| | | |
| | | A slash is appended to all resource URLs when |
| | | :meth:`~pyramid.request.Request.resource_url` is used to generate them in |
| | | this simple manner, because resources are "places" in the hierarchy, and URLs |
| | | are meant to be clicked on to be visited. Relative URLs that you include on |
| | | HTML pages rendered as the result of the default view of a resource are more |
| | | apt to be relative to these resources than relative to their parent. |
| | | :meth:`~pyramid.request.Request.resource_url` is used to generate them in this |
| | | simple manner, because resources are "places" in the hierarchy, and URLs are |
| | | meant to be clicked on to be visited. Relative URLs that you include on HTML |
| | | pages rendered as the result of the default view of a resource are more apt to |
| | | be relative to these resources than relative to their parent. |
| | | |
| | | You can also pass extra elements to |
| | | :meth:`~pyramid.request.Request.resource_url`: |
| | |
| | | url = request.resource_url(resource, 'foo', 'bar') |
| | | |
| | | If the resource referred to as ``resource`` in the above example was the root |
| | | resource, and the host that was used to contact the server was |
| | | ``example.com``, the URL generated would be ``http://example.com/foo/bar``. |
| | | Any number of extra elements can be passed to |
| | | :meth:`~pyramid.request.Request.resource_url` as extra positional arguments. |
| | | When extra elements are passed, they are appended to the resource's URL. A |
| | | slash is not appended to the final segment when elements are passed. |
| | | resource, and the host that was used to contact the server was ``example.com``, |
| | | the URL generated would be ``http://example.com/foo/bar``. Any number of extra |
| | | elements can be passed to :meth:`~pyramid.request.Request.resource_url` as |
| | | extra positional arguments. When extra elements are passed, they are appended |
| | | to the resource's URL. A slash is not appended to the final segment when |
| | | elements are passed. |
| | | |
| | | You can also pass a query string: |
| | | |
| | |
| | | url = request.resource_url(resource, query={'a':'1'}) |
| | | |
| | | If the resource referred to as ``resource`` in the above example was the root |
| | | resource, and the host that was used to contact the server was |
| | | ``example.com``, the URL generated would be ``http://example.com/?a=1``. |
| | | resource, and the host that was used to contact the server was ``example.com``, |
| | | the URL generated would be ``http://example.com/?a=1``. |
| | | |
| | | When a :term:`virtual root` is active, the URL generated by |
| | | :meth:`~pyramid.request.Request.resource_url` for a resource may be "shorter" |
| | | than its physical tree path. See :ref:`virtual_root_support` for more |
| | | information about virtually rooting a resource. |
| | | |
| | | For more information about generating resource URLs, see the documentation |
| | | for :meth:`pyramid.request.Request.resource_url`. |
| | | For more information about generating resource URLs, see the documentation for |
| | | :meth:`pyramid.request.Request.resource_url`. |
| | | |
| | | .. index:: |
| | | pair: resource URL generation; overriding |
| | |
| | | Overriding Resource URL Generation |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | If a resource object implements a ``__resource_url__`` method, this method |
| | | will be called when :meth:`~pyramid.request.Request.resource_url` is called |
| | | to generate a URL for the resource, overriding the default URL returned for |
| | | the resource by :meth:`~pyramid.request.Request.resource_url`. |
| | | If a resource object implements a ``__resource_url__`` method, this method will |
| | | be called when :meth:`~pyramid.request.Request.resource_url` is called to |
| | | generate a URL for the resource, overriding the default URL returned for the |
| | | resource by :meth:`~pyramid.request.Request.resource_url`. |
| | | |
| | | The ``__resource_url__`` hook is passed two arguments: ``request`` and |
| | | ``info``. ``request`` is the :term:`request` object passed to |
| | |
| | | |
| | | ``physical_path`` |
| | | A string representing the "physical path" computed for the resource, as |
| | | defined by ``pyramid.traversal.resource_path(resource)``. It will begin |
| | | and end with a slash. |
| | | defined by ``pyramid.traversal.resource_path(resource)``. It will begin and |
| | | end with a slash. |
| | | |
| | | ``virtual_path`` |
| | | A string representing the "virtual path" computed for the resource, as |
| | | defined by :ref:`virtual_root_support`. This will be identical to the |
| | | physical path if virtual rooting is not enabled. It will begin and end |
| | | with a slash. |
| | | physical path if virtual rooting is not enabled. It will begin and end with |
| | | a slash. |
| | | |
| | | ``app_url`` |
| | | A string representing the application URL generated during |
| | | ``request.resource_url``. It will not end with a slash. It represents a |
| | | potentially customized URL prefix, containing potentially custom scheme, |
| | | host and port information passed by the user to ``request.resource_url``. |
| | | It should be preferred over use of ``request.application_url``. |
| | | potentially customized URL prefix, containing potentially custom scheme, host |
| | | and port information passed by the user to ``request.resource_url``. It |
| | | should be preferred over use of ``request.application_url``. |
| | | |
| | | The ``__resource_url__`` method of a resource should return a string |
| | | representing a URL. If it cannot override the default, it should return |
| | |
| | | |
| | | The above example actually just generates and returns the default URL, which |
| | | would have been what was generated by the default ``resource_url`` machinery, |
| | | but your code can perform arbitrary logic as necessary. For example, your |
| | | code may wish to override the hostname or port number of the generated URL. |
| | | but your code can perform arbitrary logic as necessary. For example, your code |
| | | may wish to override the hostname or port number of the generated URL. |
| | | |
| | | Note that the URL generated by ``__resource_url__`` should be fully |
| | | qualified, should end in a slash, and should not contain any query string or |
| | | anchor elements (only path elements) to work with |
| | | Note that the URL generated by ``__resource_url__`` should be fully qualified, |
| | | should end in a slash, and should not contain any query string or anchor |
| | | elements (only path elements) to work with |
| | | :meth:`~pyramid.request.Request.resource_url`. |
| | | |
| | | .. index:: |
| | |
| | | --------------------------------- |
| | | |
| | | :func:`pyramid.traversal.resource_path` returns a string object representing |
| | | the absolute physical path of the resource object based on its position in |
| | | the resource tree. Each segment of the path is separated with a slash |
| | | character. |
| | | the absolute physical path of the resource object based on its position in the |
| | | resource tree. Each segment of the path is separated with a slash character. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | The resource passed in must be :term:`location`-aware. |
| | | |
| | | The presence or absence of a :term:`virtual root` has no impact on the |
| | | behavior of :func:`~pyramid.traversal.resource_path`. |
| | | The presence or absence of a :term:`virtual root` has no impact on the behavior |
| | | of :func:`~pyramid.traversal.resource_path`. |
| | | |
| | | .. index:: |
| | | pair: resource; finding by path |
| | |
| | | Finding a Resource by Path |
| | | -------------------------- |
| | | |
| | | If you have a string path to a resource, you can grab the resource from |
| | | that place in the application's resource tree using |
| | | If you have a string path to a resource, you can grab the resource from that |
| | | place in the application's resource tree using |
| | | :func:`pyramid.traversal.find_resource`. |
| | | |
| | | You can resolve an absolute path by passing a string prefixed with a ``/`` as |
| | |
| | | from pyramid.traversal import find_resource |
| | | url = find_resource(anyresource, '/path') |
| | | |
| | | Or you can resolve a path relative to the resource you pass in by passing a |
| | | string that isn't prefixed by ``/``: |
| | | Or you can resolve a path relative to the resource that you pass in to |
| | | :func:`pyramid.traversal.find_resource` by passing a string that isn't prefixed |
| | | by ``/``: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | url = find_resource(anyresource, 'path') |
| | | |
| | | Often the paths you pass to :func:`~pyramid.traversal.find_resource` are |
| | | generated by the :func:`~pyramid.traversal.resource_path` API. These APIs |
| | | are "mirrors" of each other. |
| | | generated by the :func:`~pyramid.traversal.resource_path` API. These APIs are |
| | | "mirrors" of each other. |
| | | |
| | | If the path cannot be resolved when calling |
| | | :func:`~pyramid.traversal.find_resource` (if the respective resource in the |
| | |
| | | ----------------------------------- |
| | | |
| | | :func:`pyramid.location.lineage` returns a generator representing the |
| | | :term:`lineage` of the :term:`location` aware :term:`resource` object. |
| | | :term:`lineage` of the :term:`location`-aware :term:`resource` object. |
| | | |
| | | The :func:`~pyramid.location.lineage` function returns the resource it is |
| | | passed, then each parent of the resource, in order. For example, if the |
| | | The :func:`~pyramid.location.lineage` function returns the resource that is |
| | | passed into it, then each parent of the resource in order. For example, if the |
| | | resource tree is composed like so: |
| | | |
| | | .. code-block:: python |
| | |
| | | list(lineage(thing2)) |
| | | [ <Thing object at thing2>, <Thing object at thing1> ] |
| | | |
| | | The generator returned by :func:`~pyramid.location.lineage` first returns the |
| | | resource it was passed unconditionally. Then, if the resource supplied a |
| | | ``__parent__`` attribute, it returns the resource represented by |
| | | ``resource.__parent__``. If *that* resource has a ``__parent__`` attribute, |
| | | return that resource's parent, and so on, until the resource being inspected |
| | | either has no ``__parent__`` attribute or has a ``__parent__`` attribute of |
| | | ``None``. |
| | | The generator returned by :func:`~pyramid.location.lineage` first returns |
| | | unconditionally the resource that was passed into it. Then, if the resource |
| | | supplied a ``__parent__`` attribute, it returns the resource represented by |
| | | ``resource.__parent__``. If *that* resource has a ``__parent__`` attribute, it |
| | | will return that resource's parent, and so on, until the resource being |
| | | inspected either has no ``__parent__`` attribute or has a ``__parent__`` |
| | | attribute of ``None``. |
| | | |
| | | See the documentation for :func:`pyramid.location.lineage` for more |
| | | information. |
| | | |
| | | Determining if a Resource is In The Lineage of Another Resource |
| | | Determining if a Resource is in the Lineage of Another Resource |
| | | --------------------------------------------------------------- |
| | | |
| | | Use the :func:`pyramid.location.inside` function to determine if one resource |
| | |
| | | b = Thing() |
| | | b.__parent__ = a |
| | | |
| | | Calling ``inside(b, a)`` will return ``True``, because ``b`` has a lineage |
| | | that includes ``a``. However, calling ``inside(a, b)`` will return ``False`` |
| | | Calling ``inside(b, a)`` will return ``True``, because ``b`` has a lineage that |
| | | includes ``a``. However, calling ``inside(a, b)`` will return ``False`` |
| | | because ``a`` does not have a lineage that includes ``b``. |
| | | |
| | | The argument list for :func:`~pyramid.location.inside` is ``(resource1, |
| | | resource2)``. ``resource1`` is 'inside' ``resource2`` if ``resource2`` is a |
| | | resource2)``. ``resource1`` is "inside" ``resource2`` if ``resource2`` is a |
| | | :term:`lineage` ancestor of ``resource1``. It is a lineage ancestor if its |
| | | parent (or one of its parent's parents, etc.) is an ancestor. |
| | | |
| | |
| | | ------------------------- |
| | | |
| | | Use the :func:`pyramid.traversal.find_root` API to find the :term:`root` |
| | | resource. The root resource is the root resource of the :term:`resource |
| | | tree`. The API accepts a single argument: ``resource``. This is a resource |
| | | that is :term:`location` aware. It can be any resource in the tree for which |
| | | resource. The root resource is the resource at the root of the :term:`resource |
| | | tree`. The API accepts a single argument: ``resource``. This is a resource |
| | | that is :term:`location`-aware. It can be any resource in the tree for which |
| | | you want to find the root. |
| | | |
| | | For example, if the resource tree is: |
| | |
| | | The root resource is also available as ``request.root`` within :term:`view |
| | | callable` code. |
| | | |
| | | The presence or absence of a :term:`virtual root` has no impact on the |
| | | behavior of :func:`~pyramid.traversal.find_root`. The root object returned |
| | | is always the *physical* root object. |
| | | The presence or absence of a :term:`virtual root` has no impact on the behavior |
| | | of :func:`~pyramid.traversal.find_root`. The root object returned is always |
| | | the *physical* root object. |
| | | |
| | | .. index:: |
| | | single: resource interfaces |
| | |
| | | ------------------------------------ |
| | | |
| | | Resources can optionally be made to implement an :term:`interface`. An |
| | | interface is used to tag a resource object with a "type" that can later be |
| | | interface is used to tag a resource object with a "type" that later can be |
| | | referred to within :term:`view configuration` and by |
| | | :func:`pyramid.traversal.find_interface`. |
| | | |
| | | Specifying an interface instead of a class as the ``context`` or |
| | | ``containment`` predicate arguments within :term:`view configuration` |
| | | statements makes it possible to use a single view callable for more than one |
| | | class of resource object. If your application is simple enough that you see |
| | | no reason to want to do this, you can skip reading this section of the |
| | | chapter. |
| | | class of resource objects. If your application is simple enough that you see |
| | | no reason to want to do this, you can skip reading this section of the chapter. |
| | | |
| | | For example, here's some code which describes a blog entry which also |
| | | declares that the blog entry implements an :term:`interface`. |
| | | For example, here's some code which describes a blog entry which also declares |
| | | that the blog entry implements an :term:`interface`. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | ``BlogEntry`` resource implements the ``IBlogEntry`` interface. |
| | | |
| | | You can also specify that a particular resource *instance* provides an |
| | | interface, as opposed to its class. When you declare that a class implements |
| | | an interface, all instances of that class will also provide that interface. |
| | | However, you can also just say that a single object provides the interface. |
| | | To do so, use the :func:`zope.interface.directlyProvides` function: |
| | | interface as opposed to its class. When you declare that a class implements an |
| | | interface, all instances of that class will also provide that interface. |
| | | However, you can also just say that a single object provides the interface. To |
| | | do so, use the :func:`zope.interface.directlyProvides` function: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | directlyProvides(entry, IBlogEntry) |
| | | |
| | | :func:`zope.interface.directlyProvides` will replace any existing interface |
| | | that was previously provided by an instance. If a resource object already |
| | | has instance-level interface declarations that you don't want to replace, use |
| | | the :func:`zope.interface.alsoProvides` function: |
| | | that was previously provided by an instance. If a resource object already has |
| | | instance-level interface declarations that you don't want to replace, use the |
| | | :func:`zope.interface.alsoProvides` function: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | directlyProvides(entry, IBlogEntry1) |
| | | alsoProvides(entry, IBlogEntry2) |
| | | |
| | | :func:`zope.interface.alsoProvides` will augment the set of interfaces |
| | | directly provided by an instance instead of overwriting them like |
| | | :func:`zope.interface.alsoProvides` will augment the set of interfaces directly |
| | | provided by an instance instead of overwriting them like |
| | | :func:`zope.interface.directlyProvides` does. |
| | | |
| | | For more information about how resource interfaces can be used by view |
| | |
| | | .. index:: |
| | | pair: resource; finding by interface or class |
| | | |
| | | Finding a Resource With a Class or Interface in Lineage |
| | | Finding a Resource with a Class or Interface in Lineage |
| | | ------------------------------------------------------- |
| | | |
| | | Use the :func:`~pyramid.traversal.find_interface` API to locate a parent that |
| | |
| | | |
| | | Calling ``find_interface(a, Thing1)`` will return the ``a`` resource because |
| | | ``a`` is of class ``Thing1`` (the resource passed as the first argument is |
| | | considered first, and is returned if the class or interface spec matches). |
| | | considered first, and is returned if the class or interface specification |
| | | matches). |
| | | |
| | | Calling ``find_interface(b, Thing1)`` will return the ``a`` resource because |
| | | ``a`` is of class ``Thing1`` and ``a`` is the first resource in ``b``'s |
| | | lineage of this class. |
| | | ``a`` is of class ``Thing1`` and ``a`` is the first resource in ``b``'s lineage |
| | | of this class. |
| | | |
| | | Calling ``find_interface(b, Thing2)`` will return the ``b`` resource. |
| | | |
| | | The second argument to find_interface may also be a :term:`interface` instead |
| | | of a class. If it is an interface, each resource in the lineage is checked |
| | | to see if the resource implements the specificed interface (instead of seeing |
| | | if the resource is of a class). |
| | | The second argument to ``find_interface`` may also be a :term:`interface` |
| | | instead of a class. If it is an interface, each resource in the lineage is |
| | | checked to see if the resource implements the specificed interface (instead of |
| | | seeing if the resource is of a class). |
| | | |
| | | .. seealso:: |
| | | |
| | |
| | | :ref:`traversal_chapter` and :ref:`urldispatch_chapter` for more information |
| | | about how a resource object becomes the context. |
| | | |
| | | The APIs provided by :ref:`traversal_module` are used against resource |
| | | objects. These functions can be used to find the "path" of a resource, the |
| | | root resource in a resource tree, or to generate a URL for a resource. |
| | | The APIs provided by :ref:`traversal_module` are used against resource objects. |
| | | These functions can be used to find the "path" of a resource, the root resource |
| | | in a resource tree, or to generate a URL for a resource. |
| | | |
| | | The APIs provided by :ref:`location_module` are used against resources. |
| | | These can be used to walk down a resource tree, or conveniently locate one |
| | | resource "inside" another. |
| | | The APIs provided by :ref:`location_module` are used against resources. These |
| | | can be used to walk down a resource tree, or conveniently locate one resource |
| | | "inside" another. |
| | | |
| | | Some APIs on the :class:`pyramid.request.Request` accept a resource object as a parameter. |
| | | For example, the :meth:`~pyramid.request.Request.has_permission` API accepts a |
| | | resource object as one of its arguments; the ACL is obtained from this |
| | | resource or one of its ancestors. Other security related APIs on the |
| | | :class:`pyramid.request.Request` class also accept :term:`context` as an argument, |
| | | and a context is always a resource. |
| | | |
| | | Some APIs on the :class:`pyramid.request.Request` accept a resource object as a |
| | | parameter. For example, the :meth:`~pyramid.request.Request.has_permission` API |
| | | accepts a resource object as one of its arguments; the ACL is obtained from |
| | | this resource or one of its ancestors. Other security related APIs on the |
| | | :class:`pyramid.request.Request` class also accept :term:`context` as an |
| | | argument, and a context is always a resource. |
| | |
| | | ========================== |
| | | |
| | | You can extend Pyramid by creating a :term:`scaffold` template. A scaffold |
| | | template is useful if you'd like to distribute a customizable configuration |
| | | of Pyramid to other users. Once you've created a scaffold, and someone has |
| | | template is useful if you'd like to distribute a customizable configuration of |
| | | Pyramid to other users. Once you've created a scaffold, and someone has |
| | | installed the distribution that houses the scaffold, they can use the |
| | | ``pcreate`` script to create a custom version of your scaffold's template. |
| | | Pyramid itself uses scaffolds to allow people to bootstrap new projects. For |
| | |
| | | Basics |
| | | ------ |
| | | |
| | | A scaffold template is just a bunch of source files and directories on disk. |
| | | A small definition class points at this directory; it is in turn pointed at |
| | | by a :term:`setuptools` "entry point" which registers the scaffold so it can |
| | | be found by the ``pcreate`` command. |
| | | A scaffold template is just a bunch of source files and directories on disk. A |
| | | small definition class points at this directory. It is in turn pointed at by a |
| | | :term:`setuptools` "entry point" which registers the scaffold so it can be |
| | | found by the ``pcreate`` command. |
| | | |
| | | To create a scaffold template, create a Python :term:`distribution` to house |
| | | the scaffold which includes a ``setup.py`` that relies on the ``setuptools`` |
| | | package. See `Creating a Package |
| | | <http://guide.python-distribute.org/creation.html>`_ for more information |
| | | about how to do this. For the sake of example, we'll pretend the |
| | | distribution you create is named ``CoolExtension``, and it has a package |
| | | directory within it named ``coolextension`` |
| | | package. See `Packaging and Distributing Projects |
| | | <https://packaging.python.org/en/latest/distributing/>`_ for more information |
| | | about how to do this. For example, we'll pretend the distribution you create |
| | | is named ``CoolExtension``, and it has a package directory within it named |
| | | ``coolextension``. |
| | | |
| | | Once you've created the distribution put a "scaffolds" directory within your |
| | | distribution's package directory, and create a file within that directory |
| | | named ``__init__.py`` with something like the following: |
| | | Once you've created the distribution, put a "scaffolds" directory within your |
| | | distribution's package directory, and create a file within that directory named |
| | | ``__init__.py`` with something like the following: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | the string value of the variable named ``var`` provided to the scaffold. |
| | | |
| | | - Files and directories with filenames that contain the string ``+var+`` will |
| | | have that string replaced with the value of the ``var`` variable provided |
| | | to the scaffold. |
| | | have that string replaced with the value of the ``var`` variable provided to |
| | | the scaffold. |
| | | |
| | | - Files that start with a dot (e.g., ``.env``) are ignored and will not be |
| | | copied over to the destination directory. If you want to include a file with |
| | | a leading dot then you must replace the dot with ``+dot+`` (e.g., |
| | | a leading dot, then you must replace the dot with ``+dot+`` (e.g., |
| | | ``+dot+env``). |
| | | |
| | | Otherwise, files and directories which live in the template directory will be |
| | |
| | | |
| | | The variables provided by the default ``PyramidTemplate`` include ``project`` |
| | | (the project name provided by the user as an argument to ``pcreate``), |
| | | ``package`` (a lowercasing and normalizing of the project name provided by |
| | | the user), ``random_string`` (a long random string), and ``package_logger`` |
| | | (the name of the package's logger). |
| | | ``package`` (a lowercasing and normalizing of the project name provided by the |
| | | user), ``random_string`` (a long random string), and ``package_logger`` (the |
| | | name of the package's logger). |
| | | |
| | | See Pyramid's "scaffolds" package |
| | | (https://github.com/Pylons/pyramid/tree/master/pyramid/scaffolds) for |
| | | concrete examples of scaffold directories (``zodb``, ``alchemy``, and |
| | | ``starter``, for example). |
| | | (https://github.com/Pylons/pyramid/tree/master/pyramid/scaffolds) for concrete |
| | | examples of scaffold directories (``zodb``, ``alchemy``, and ``starter``, for |
| | | example). |
| | | |
| | | After you've created the template directory, add the following to the |
| | | ``entry_points`` value of your distribution's ``setup.py``: |
| | |
| | | """ |
| | | ) |
| | | |
| | | Run your distribution's ``setup.py develop`` or ``setup.py install`` |
| | | command. After that, you should be able to see your scaffolding template |
| | | listed when you run ``pcreate -l``. It will be named ``coolextension`` |
| | | because that's the name we gave it in the entry point setup. Running |
| | | ``pcreate -s coolextension MyStuff`` will then render your scaffold to an |
| | | output directory named ``MyStuff``. |
| | | Run your distribution's ``setup.py develop`` or ``setup.py install`` command. |
| | | After that, you should be able to see your scaffolding template listed when you |
| | | run ``pcreate -l``. It will be named ``coolextension`` because that's the name |
| | | we gave it in the entry point setup. Running ``pcreate -s coolextension |
| | | MyStuff`` will then render your scaffold to an output directory named |
| | | ``MyStuff``. |
| | | |
| | | See the module documentation for :mod:`pyramid.scaffolds` for information |
| | | about the API of the :class:`pyramid.scaffolds.Template` class and |
| | | related classes. You can override methods of this class to get special |
| | | behavior. |
| | | See the module documentation for :mod:`pyramid.scaffolds` for information about |
| | | the API of the :class:`pyramid.scaffolds.Template` class and related classes. |
| | | You can override methods of this class to get special behavior. |
| | | |
| | | Supporting Older Pyramid Versions |
| | | --------------------------------- |
| | |
| | | |
| | | And then in the setup.py of the package that contains your scaffold, define |
| | | the template as a target of both ``paste.paster_create_template`` (for |
| | | ``paster create``) and ``pyramid.scaffold`` (for ``pcreate``):: |
| | | ``paster create``) and ``pyramid.scaffold`` (for ``pcreate``). |
| | | |
| | | [paste.paster_create_template] |
| | | coolextension=coolextension.scaffolds:CoolExtensionTemplate |
| | | [pyramid.scaffold] |
| | | coolextension=coolextension.scaffolds:CoolExtensionTemplate |
| | | .. code-block:: ini |
| | | |
| | | Doing this hideousness will allow your scaffold to work as a ``paster |
| | | create`` target (under 1.0, 1.1, or 1.2) or as a ``pcreate`` target (under |
| | | 1.3). If an invoker tries to run ``paster create`` against a scaffold |
| | | defined this way under 1.3, an error is raised instructing them to use |
| | | ``pcreate`` instead. |
| | | [paste.paster_create_template] |
| | | coolextension=coolextension.scaffolds:CoolExtensionTemplate |
| | | [pyramid.scaffold] |
| | | coolextension=coolextension.scaffolds:CoolExtensionTemplate |
| | | |
| | | If you want only to support Pyramid 1.3 only, it's much cleaner, and the API |
| | | is stable: |
| | | Doing this hideousness will allow your scaffold to work as a ``paster create`` |
| | | target (under 1.0, 1.1, or 1.2) or as a ``pcreate`` target (under 1.3). If an |
| | | invoker tries to run ``paster create`` against a scaffold defined this way |
| | | under 1.3, an error is raised instructing them to use ``pcreate`` instead. |
| | | |
| | | If you want to support Pyramid 1.3 only, it's much cleaner, and the API is |
| | | stable: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | _template_dir = 'coolextension_scaffold' |
| | | summary = 'My cool_extension' |
| | | |
| | | You only need to specify a ``paste.paster_create_template`` entry point |
| | | target in your ``setup.py`` if you want your scaffold to be consumable by |
| | | users of Pyramid 1.0, 1.1, or 1.2. To support only 1.3, specifying only the |
| | | You only need to specify a ``paste.paster_create_template`` entry point target |
| | | in your ``setup.py`` if you want your scaffold to be consumable by users of |
| | | Pyramid 1.0, 1.1, or 1.2. To support only 1.3, specifying only the |
| | | ``pyramid.scaffold`` entry point is good enough. If you want to support both |
| | | ``paster create`` and ``pcreate`` (meaning you want to support Pyramid 1.2 |
| | | and some older version), you'll need to define both. |
| | | ``paster create`` and ``pcreate`` (meaning you want to support Pyramid 1.2 and |
| | | some older version), you'll need to define both. |
| | | |
| | | Examples |
| | | -------- |
| | | |
| | | Existing third-party distributions which house scaffolding are available via |
| | | :term:`PyPI`. The ``pyramid_jqm``, ``pyramid_zcml`` and ``pyramid_jinja2`` |
| | | :term:`PyPI`. The ``pyramid_jqm``, ``pyramid_zcml``, and ``pyramid_jinja2`` |
| | | packages house scaffolds. You can install and examine these packages to see |
| | | how they work in the quest to develop your own scaffolding. |
| | |
| | | Security |
| | | ======== |
| | | |
| | | :app:`Pyramid` provides an optional, declarative, security system. |
| | | Security in :app:`Pyramid` is separated into authentication and |
| | | authorization. The two systems communicate via :term:`principal` |
| | | identifiers. Authentication is merely the mechanism by which credentials |
| | | provided in the :term:`request` are resolved to one or more |
| | | :term:`principal` identifiers. These identifiers represent the users and |
| | | groups that are in effect during the request. Authorization then determines |
| | | access based on the :term:`principal` identifiers, the requested |
| | | :app:`Pyramid` provides an optional, declarative, security system. Security in |
| | | :app:`Pyramid` is separated into authentication and authorization. The two |
| | | systems communicate via :term:`principal` identifiers. Authentication is merely |
| | | the mechanism by which credentials provided in the :term:`request` are resolved |
| | | to one or more :term:`principal` identifiers. These identifiers represent the |
| | | users and groups that are in effect during the request. Authorization then |
| | | determines access based on the :term:`principal` identifiers, the requested |
| | | :term:`permission`, and a :term:`context`. |
| | | |
| | | The :app:`Pyramid` authorization system |
| | | can prevent a :term:`view` from being invoked based on an |
| | | :term:`authorization policy`. Before a view is invoked, the |
| | | authorization system can use the credentials in the :term:`request` |
| | | along with the :term:`context` resource to determine if access will be |
| | | allowed. Here's how it works at a high level: |
| | | The :app:`Pyramid` authorization system can prevent a :term:`view` from being |
| | | invoked based on an :term:`authorization policy`. Before a view is invoked, the |
| | | authorization system can use the credentials in the :term:`request` along with |
| | | the :term:`context` resource to determine if access will be allowed. Here's |
| | | how it works at a high level: |
| | | |
| | | - A user may or may not have previously visited the application and |
| | | supplied authentication credentials, including a :term:`userid`. If |
| | | so, the application may have called |
| | | :func:`pyramid.security.remember` to remember these. |
| | | - A user may or may not have previously visited the application and supplied |
| | | authentication credentials, including a :term:`userid`. If so, the |
| | | application may have called :func:`pyramid.security.remember` to remember |
| | | these. |
| | | |
| | | - A :term:`request` is generated when a user visits the application. |
| | | |
| | | - Based on the request, a :term:`context` resource is located through |
| | | :term:`resource location`. A context is located differently depending on |
| | | whether the application uses :term:`traversal` or :term:`URL dispatch`, but |
| | | a context is ultimately found in either case. See |
| | | the :ref:`urldispatch_chapter` chapter for more information. |
| | | whether the application uses :term:`traversal` or :term:`URL dispatch`, but a |
| | | context is ultimately found in either case. See the |
| | | :ref:`urldispatch_chapter` chapter for more information. |
| | | |
| | | - A :term:`view callable` is located by :term:`view lookup` using the |
| | | context as well as other attributes of the request. |
| | | - A :term:`view callable` is located by :term:`view lookup` using the context |
| | | as well as other attributes of the request. |
| | | |
| | | - If an :term:`authentication policy` is in effect, it is passed the |
| | | request. It will return some number of :term:`principal` identifiers. |
| | | To do this, the policy would need to determine the authenticated |
| | | :term:`userid` present in the request. |
| | | - If an :term:`authentication policy` is in effect, it is passed the request. |
| | | It will return some number of :term:`principal` identifiers. To do this, the |
| | | policy would need to determine the authenticated :term:`userid` present in |
| | | the request. |
| | | |
| | | - If an :term:`authorization policy` is in effect and the :term:`view |
| | | configuration` associated with the view callable that was found has |
| | | a :term:`permission` associated with it, the authorization policy is |
| | | passed the :term:`context`, some number of :term:`principal` |
| | | identifiers returned by the authentication policy, and the |
| | | :term:`permission` associated with the view; it will allow or deny |
| | | access. |
| | | configuration` associated with the view callable that was found has a |
| | | :term:`permission` associated with it, the authorization policy is passed the |
| | | :term:`context`, some number of :term:`principal` identifiers returned by the |
| | | authentication policy, and the :term:`permission` associated with the view; |
| | | it will allow or deny access. |
| | | |
| | | - If the authorization policy allows access, the view callable is |
| | | invoked. |
| | | - If the authorization policy allows access, the view callable is invoked. |
| | | |
| | | - If the authorization policy denies access, the view callable is not |
| | | invoked; instead the :term:`forbidden view` is invoked. |
| | | - If the authorization policy denies access, the view callable is not invoked. |
| | | Instead the :term:`forbidden view` is invoked. |
| | | |
| | | Authorization is enabled by modifying your application to include an |
| | | :term:`authentication policy` and :term:`authorization policy`. |
| | | :app:`Pyramid` comes with a variety of implementations of these |
| | | policies. To provide maximal flexibility, :app:`Pyramid` also |
| | | allows you to create custom authentication policies and authorization |
| | | policies. |
| | | :term:`authentication policy` and :term:`authorization policy`. :app:`Pyramid` |
| | | comes with a variety of implementations of these policies. To provide maximal |
| | | flexibility, :app:`Pyramid` also allows you to create custom authentication |
| | | policies and authorization policies. |
| | | |
| | | .. index:: |
| | | single: authorization policy |
| | |
| | | Enabling an Authorization Policy |
| | | -------------------------------- |
| | | |
| | | :app:`Pyramid` does not enable any authorization policy by default. All |
| | | views are accessible by completely anonymous users. In order to begin |
| | | protecting views from execution based on security settings, you need |
| | | to enable an authorization policy. |
| | | :app:`Pyramid` does not enable any authorization policy by default. All views |
| | | are accessible by completely anonymous users. In order to begin protecting |
| | | views from execution based on security settings, you need to enable an |
| | | authorization policy. |
| | | |
| | | Enabling an Authorization Policy Imperatively |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Use the :meth:`~pyramid.config.Configurator.set_authorization_policy` method |
| | | of the :class:`~pyramid.config.Configurator` to enable an authorization |
| | | policy. |
| | | Use the :meth:`~pyramid.config.Configurator.set_authorization_policy` method of |
| | | the :class:`~pyramid.config.Configurator` to enable an authorization policy. |
| | | |
| | | You must also enable an :term:`authentication policy` in order to enable the |
| | | authorization policy. This is because authorization, in general, depends |
| | | upon authentication. Use the |
| | | :meth:`~pyramid.config.Configurator.set_authentication_policy` method |
| | | during application setup to specify the authentication policy. |
| | | authorization policy. This is because authorization, in general, depends upon |
| | | authentication. Use the |
| | | :meth:`~pyramid.config.Configurator.set_authentication_policy` method during |
| | | application setup to specify the authentication policy. |
| | | |
| | | For example: |
| | | |
| | |
| | | config.set_authentication_policy(authn_policy) |
| | | config.set_authorization_policy(authz_policy) |
| | | |
| | | .. note:: The ``authentication_policy`` and ``authorization_policy`` |
| | | arguments may also be passed to their respective methods mentioned above |
| | | as :term:`dotted Python name` values, each representing the dotted name |
| | | path to a suitable implementation global defined at Python module scope. |
| | | .. note:: The ``authentication_policy`` and ``authorization_policy`` arguments |
| | | may also be passed to their respective methods mentioned above as |
| | | :term:`dotted Python name` values, each representing the dotted name path to |
| | | a suitable implementation global defined at Python module scope. |
| | | |
| | | The above configuration enables a policy which compares the value of an "auth |
| | | ticket" cookie passed in the request's environment which contains a reference |
| | | to a single :term:`userid` and matches that userid's |
| | | :term:`principals <principal>` against the principals present in any |
| | | :term:`ACL` found in the resource tree when attempting to call some |
| | | :term:`view`. |
| | | to a single :term:`userid`, and matches that userid's :term:`principals |
| | | <principal>` against the principals present in any :term:`ACL` found in the |
| | | resource tree when attempting to call some :term:`view`. |
| | | |
| | | While it is possible to mix and match different authentication and |
| | | authorization policies, it is an error to configure a Pyramid application |
| | | with an authentication policy but without the authorization policy or vice |
| | | versa. If you do this, you'll receive an error at application startup time. |
| | | authorization policies, it is an error to configure a Pyramid application with |
| | | an authentication policy but without the authorization policy or vice versa. If |
| | | you do this, you'll receive an error at application startup time. |
| | | |
| | | .. seealso:: |
| | | |
| | | See also the :mod:`pyramid.authorization` and |
| | | :mod:`pyramid.authentication` modules for alternate implementations of |
| | | authorization and authentication policies. |
| | | See also the :mod:`pyramid.authorization` and :mod:`pyramid.authentication` |
| | | modules for alternative implementations of authorization and authentication |
| | | policies. |
| | | |
| | | .. index:: |
| | | single: permissions |
| | |
| | | |
| | | To protect a :term:`view callable` from invocation based on a user's security |
| | | settings when a particular type of resource becomes the :term:`context`, you |
| | | must pass a :term:`permission` to :term:`view configuration`. Permissions |
| | | are usually just strings, and they have no required composition: you can name |
| | | must pass a :term:`permission` to :term:`view configuration`. Permissions are |
| | | usually just strings, and they have no required composition: you can name |
| | | permissions whatever you like. |
| | | |
| | | For example, the following view declaration protects the view named |
| | | ``add_entry.html`` when the context resource is of type ``Blog`` with the |
| | | ``add`` permission using the :meth:`pyramid.config.Configurator.add_view` |
| | | API: |
| | | ``add`` permission using the :meth:`pyramid.config.Configurator.add_view` API: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | context='mypackage.resources.Blog', |
| | | permission='add') |
| | | |
| | | The equivalent view registration including the ``add`` permission name |
| | | may be performed via the ``@view_config`` decorator: |
| | | The equivalent view registration including the ``add`` permission name may be |
| | | performed via the ``@view_config`` decorator: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | pass |
| | | |
| | | As a result of any of these various view configuration statements, if an |
| | | authorization policy is in place when the view callable is found during |
| | | normal application operations, the requesting user will need to possess the |
| | | ``add`` permission against the :term:`context` resource in order to be able |
| | | to invoke the ``blog_entry_add_view`` view. If he does not, the |
| | | :term:`Forbidden view` will be invoked. |
| | | authorization policy is in place when the view callable is found during normal |
| | | application operations, the requesting user will need to possess the ``add`` |
| | | permission against the :term:`context` resource in order to be able to invoke |
| | | the ``blog_entry_add_view`` view. If they do not, the :term:`Forbidden view` |
| | | will be invoked. |
| | | |
| | | .. index:: |
| | | pair: permission; default |
| | |
| | | Setting a Default Permission |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | If a permission is not supplied to a view configuration, the registered |
| | | view will always be executable by entirely anonymous users: any |
| | | authorization policy in effect is ignored. |
| | | If a permission is not supplied to a view configuration, the registered view |
| | | will always be executable by entirely anonymous users: any authorization policy |
| | | in effect is ignored. |
| | | |
| | | In support of making it easier to configure applications which are |
| | | "secure by default", :app:`Pyramid` allows you to configure a |
| | | *default* permission. If supplied, the default permission is used as |
| | | the permission string to all view registrations which don't otherwise |
| | | name a ``permission`` argument. |
| | | In support of making it easier to configure applications which are "secure by |
| | | default", :app:`Pyramid` allows you to configure a *default* permission. If |
| | | supplied, the default permission is used as the permission string to all view |
| | | registrations which don't otherwise name a ``permission`` argument. |
| | | |
| | | The :meth:`pyramid.config.Configurator.set_default_permission` method |
| | | supports configuring a default permission for an application. |
| | | The :meth:`pyramid.config.Configurator.set_default_permission` method supports |
| | | configuring a default permission for an application. |
| | | |
| | | When a default permission is registered: |
| | | |
| | |
| | | view-configuration-named permission is used. |
| | | |
| | | - If a view configuration names the permission |
| | | :data:`pyramid.security.NO_PERMISSION_REQUIRED`, the default permission |
| | | is ignored, and the view is registered *without* a permission (making it |
| | | :data:`pyramid.security.NO_PERMISSION_REQUIRED`, the default permission is |
| | | ignored, and the view is registered *without* a permission (making it |
| | | available to all callers regardless of their credentials). |
| | | |
| | | .. warning:: |
| | |
| | | |
| | | .. _assigning_acls: |
| | | |
| | | Assigning ACLs to your Resource Objects |
| | | Assigning ACLs to Your Resource Objects |
| | | --------------------------------------- |
| | | |
| | | When the default :app:`Pyramid` :term:`authorization policy` determines |
| | | whether a user possesses a particular permission with respect to a resource, |
| | | it examines the :term:`ACL` associated with the resource. An ACL is |
| | | associated with a resource by adding an ``__acl__`` attribute to the resource |
| | | object. This attribute can be defined on the resource *instance* if you need |
| | | When the default :app:`Pyramid` :term:`authorization policy` determines whether |
| | | a user possesses a particular permission with respect to a resource, it |
| | | examines the :term:`ACL` associated with the resource. An ACL is associated |
| | | with a resource by adding an ``__acl__`` attribute to the resource object. |
| | | This attribute can be defined on the resource *instance* if you need |
| | | instance-level security, or it can be defined on the resource *class* if you |
| | | just need type-level security. |
| | | |
| | | For example, an ACL might be attached to the resource for a blog via its |
| | | class: |
| | | For example, an ACL might be attached to the resource for a blog via its class: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | (Allow, 'group:editors', 'edit'), |
| | | ] |
| | | |
| | | Whether an ACL is attached to a resource's class or an instance of the |
| | | resource itself, the effect is the same. It is useful to decorate individual |
| | | resource instances with an ACL (as opposed to just decorating their class) in |
| | | applications such as "CMS" systems where fine-grained access is required on |
| | | an object-by-object basis. |
| | | Whether an ACL is attached to a resource's class or an instance of the resource |
| | | itself, the effect is the same. It is useful to decorate individual resource |
| | | instances with an ACL (as opposed to just decorating their class) in |
| | | applications such as content management systems where fine-grained access is |
| | | required on an object-by-object basis. |
| | | |
| | | Dynamic ACLs are also possible by turning the ACL into a callable on the |
| | | resource. This may allow the ACL to dynamically generate rules based on |
| | |
| | | (Allow, 'group:editors', 'edit'), |
| | | ] |
| | | |
| | | The example ACL indicates that the |
| | | :data:`pyramid.security.Everyone` principal -- a special |
| | | system-defined principal indicating, literally, everyone -- is allowed |
| | | to view the blog, the ``group:editors`` principal is allowed to add to |
| | | and edit the blog. |
| | | The example ACL indicates that the :data:`pyramid.security.Everyone` |
| | | principal—a special system-defined principal indicating, literally, everyone—is |
| | | allowed to view the blog, and the ``group:editors`` principal is allowed to add |
| | | to and edit the blog. |
| | | |
| | | Each element of an ACL is an :term:`ACE` or access control entry. |
| | | For example, in the above code block, there are three ACEs: ``(Allow, |
| | | Everyone, 'view')``, ``(Allow, 'group:editors', 'add')``, and |
| | | ``(Allow, 'group:editors', 'edit')``. |
| | | Each element of an ACL is an :term:`ACE`, or access control entry. For example, |
| | | in the above code block, there are three ACEs: ``(Allow, Everyone, 'view')``, |
| | | ``(Allow, 'group:editors', 'add')``, and ``(Allow, 'group:editors', 'edit')``. |
| | | |
| | | The first element of any ACE is either |
| | | :data:`pyramid.security.Allow`, or |
| | | :data:`pyramid.security.Deny`, representing the action to take when |
| | | the ACE matches. The second element is a :term:`principal`. The |
| | | third argument is a permission or sequence of permission names. |
| | | The first element of any ACE is either :data:`pyramid.security.Allow`, or |
| | | :data:`pyramid.security.Deny`, representing the action to take when the ACE |
| | | matches. The second element is a :term:`principal`. The third argument is a |
| | | permission or sequence of permission names. |
| | | |
| | | A principal is usually a user id, however it also may be a group id if your |
| | | authentication system provides group information and the effective |
| | | :term:`authentication policy` policy is written to respect group information. |
| | | See :ref:`extending_default_authentication_policies`. |
| | | |
| | | Each ACE in an ACL is processed by an authorization policy *in the |
| | | order dictated by the ACL*. So if you have an ACL like this: |
| | | Each ACE in an ACL is processed by an authorization policy *in the order |
| | | dictated by the ACL*. So if you have an ACL like this: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | (Deny, Everyone, 'view'), |
| | | ] |
| | | |
| | | The default authorization policy will *allow* everyone the view |
| | | permission, even though later in the ACL you have an ACE that denies |
| | | everyone the view permission. On the other hand, if you have an ACL |
| | | like this: |
| | | The default authorization policy will *allow* everyone the view permission, |
| | | even though later in the ACL you have an ACE that denies everyone the view |
| | | permission. On the other hand, if you have an ACL like this: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | (Allow, Everyone, 'view'), |
| | | ] |
| | | |
| | | The authorization policy will deny everyone the view permission, even |
| | | though later in the ACL is an ACE that allows everyone. |
| | | The authorization policy will deny everyone the view permission, even though |
| | | later in the ACL, there is an ACE that allows everyone. |
| | | |
| | | The third argument in an ACE can also be a sequence of permission |
| | | names instead of a single permission name. So instead of creating |
| | | multiple ACEs representing a number of different permission grants to |
| | | a single ``group:editors`` group, we can collapse this into a single |
| | | ACE, as below. |
| | | The third argument in an ACE can also be a sequence of permission names instead |
| | | of a single permission name. So instead of creating multiple ACEs representing |
| | | a number of different permission grants to a single ``group:editors`` group, we |
| | | can collapse this into a single ACE, as below. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | Special Principal Names |
| | | ----------------------- |
| | | |
| | | Special principal names exist in the :mod:`pyramid.security` |
| | | module. They can be imported for use in your own code to populate |
| | | ACLs, e.g. :data:`pyramid.security.Everyone`. |
| | | Special principal names exist in the :mod:`pyramid.security` module. They can |
| | | be imported for use in your own code to populate ACLs, e.g., |
| | | :data:`pyramid.security.Everyone`. |
| | | |
| | | :data:`pyramid.security.Everyone` |
| | | |
| | | Literally, everyone, no matter what. This object is actually a |
| | | string "under the hood" (``system.Everyone``). Every user "is" the |
| | | principal named Everyone during every request, even if a security |
| | | policy is not in use. |
| | | Literally, everyone, no matter what. This object is actually a string under |
| | | the hood (``system.Everyone``). Every user *is* the principal named |
| | | "Everyone" during every request, even if a security policy is not in use. |
| | | |
| | | :data:`pyramid.security.Authenticated` |
| | | |
| | | Any user with credentials as determined by the current security |
| | | policy. You might think of it as any user that is "logged in". |
| | | This object is actually a string "under the hood" |
| | | (``system.Authenticated``). |
| | | Any user with credentials as determined by the current security policy. You |
| | | might think of it as any user that is "logged in". This object is actually a |
| | | string under the hood (``system.Authenticated``). |
| | | |
| | | .. index:: |
| | | single: permission names |
| | |
| | | Special Permissions |
| | | ------------------- |
| | | |
| | | Special permission names exist in the :mod:`pyramid.security` |
| | | module. These can be imported for use in ACLs. |
| | | Special permission names exist in the :mod:`pyramid.security` module. These |
| | | can be imported for use in ACLs. |
| | | |
| | | .. _all_permissions: |
| | | |
| | | :data:`pyramid.security.ALL_PERMISSIONS` |
| | | |
| | | An object representing, literally, *all* permissions. Useful in an |
| | | ACL like so: ``(Allow, 'fred', ALL_PERMISSIONS)``. The |
| | | ``ALL_PERMISSIONS`` object is actually a stand-in object that has a |
| | | ``__contains__`` method that always returns ``True``, which, for all |
| | | known authorization policies, has the effect of indicating that a |
| | | given principal "has" any permission asked for by the system. |
| | | An object representing, literally, *all* permissions. Useful in an ACL like |
| | | so: ``(Allow, 'fred', ALL_PERMISSIONS)``. The ``ALL_PERMISSIONS`` object is |
| | | actually a stand-in object that has a ``__contains__`` method that always |
| | | returns ``True``, which, for all known authorization policies, has the effect |
| | | of indicating that a given principal has any permission asked for by the |
| | | system. |
| | | |
| | | .. index:: |
| | | single: special ACE |
| | |
| | | |
| | | A convenience :term:`ACE` is defined representing a deny to everyone of all |
| | | permissions in :data:`pyramid.security.DENY_ALL`. This ACE is often used as |
| | | the *last* ACE of an ACL to explicitly cause inheriting authorization |
| | | policies to "stop looking up the traversal tree" (effectively breaking any |
| | | inheritance). For example, an ACL which allows *only* ``fred`` the view |
| | | permission for a particular resource despite what inherited ACLs may say when |
| | | the default authorization policy is in effect might look like so: |
| | | the *last* ACE of an ACL to explicitly cause inheriting authorization policies |
| | | to "stop looking up the traversal tree" (effectively breaking any inheritance). |
| | | For example, an ACL which allows *only* ``fred`` the view permission for a |
| | | particular resource, despite what inherited ACLs may say when the default |
| | | authorization policy is in effect, might look like so: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | __acl__ = [ (Allow, 'fred', 'view'), DENY_ALL ] |
| | | |
| | | "Under the hood", the :data:`pyramid.security.DENY_ALL` ACE equals |
| | | the following: |
| | | Under the hood, the :data:`pyramid.security.DENY_ALL` ACE equals the |
| | | following: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | While the default :term:`authorization policy` is in place, if a resource |
| | | object does not have an ACL when it is the context, its *parent* is consulted |
| | | for an ACL. If that object does not have an ACL, *its* parent is consulted |
| | | for an ACL, ad infinitum, until we've reached the root and there are no more |
| | | for an ACL. If that object does not have an ACL, *its* parent is consulted for |
| | | an ACL, ad infinitum, until we've reached the root and there are no more |
| | | parents left. |
| | | |
| | | In order to allow the security machinery to perform ACL inheritance, resource |
| | | objects must provide *location-awareness*. Providing *location-awareness* |
| | | means two things: the root object in the resource tree must have a |
| | | ``__name__`` attribute and a ``__parent__`` attribute. |
| | | means two things: the root object in the resource tree must have a ``__name__`` |
| | | attribute and a ``__parent__`` attribute. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | __name__ = '' |
| | | __parent__ = None |
| | | |
| | | An object with a ``__parent__`` attribute and a ``__name__`` attribute |
| | | is said to be *location-aware*. Location-aware objects define an |
| | | ``__parent__`` attribute which points at their parent object. The |
| | | root object's ``__parent__`` is ``None``. |
| | | An object with a ``__parent__`` attribute and a ``__name__`` attribute is said |
| | | to be *location-aware*. Location-aware objects define a ``__parent__`` |
| | | attribute which points at their parent object. The root object's |
| | | ``__parent__`` is ``None``. |
| | | |
| | | .. seealso:: |
| | | |
| | |
| | | Changing the Forbidden View |
| | | --------------------------- |
| | | |
| | | When :app:`Pyramid` denies a view invocation due to an |
| | | authorization denial, the special ``forbidden`` view is invoked. "Out |
| | | of the box", this forbidden view is very plain. See |
| | | :ref:`changing_the_forbidden_view` within :ref:`hooks_chapter` for |
| | | instructions on how to create a custom forbidden view and arrange for |
| | | it to be called when view authorization is denied. |
| | | When :app:`Pyramid` denies a view invocation due to an authorization denial, |
| | | the special ``forbidden`` view is invoked. Out of the box, this forbidden view |
| | | is very plain. See :ref:`changing_the_forbidden_view` within |
| | | :ref:`hooks_chapter` for instructions on how to create a custom forbidden view |
| | | and arrange for it to be called when view authorization is denied. |
| | | |
| | | .. index:: |
| | | single: debugging authorization failures |
| | |
| | | Debugging View Authorization Failures |
| | | ------------------------------------- |
| | | |
| | | If your application in your judgment is allowing or denying view |
| | | access inappropriately, start your application under a shell using the |
| | | If your application in your judgment is allowing or denying view access |
| | | inappropriately, start your application under a shell using the |
| | | ``PYRAMID_DEBUG_AUTHORIZATION`` environment variable set to ``1``. For |
| | | example: |
| | | |
| | |
| | | |
| | | $ PYRAMID_DEBUG_AUTHORIZATION=1 $VENV/bin/pserve myproject.ini |
| | | |
| | | When any authorization takes place during a top-level view rendering, |
| | | a message will be logged to the console (to stderr) about what ACE in |
| | | which ACL permitted or denied the authorization based on |
| | | authentication information. |
| | | When any authorization takes place during a top-level view rendering, a message |
| | | will be logged to the console (to stderr) about what ACE in which ACL permitted |
| | | or denied the authorization based on authentication information. |
| | | |
| | | This behavior can also be turned on in the application ``.ini`` file |
| | | by setting the ``pyramid.debug_authorization`` key to ``true`` within the |
| | | application's configuration section, e.g.: |
| | | This behavior can also be turned on in the application ``.ini`` file by setting |
| | | the ``pyramid.debug_authorization`` key to ``true`` within the application's |
| | | configuration section, e.g.: |
| | | |
| | | .. code-block:: ini |
| | | :linenos: |
| | |
| | | use = egg:MyProject |
| | | pyramid.debug_authorization = true |
| | | |
| | | With this debug flag turned on, the response sent to the browser will |
| | | also contain security debugging information in its body. |
| | | With this debug flag turned on, the response sent to the browser will also |
| | | contain security debugging information in its body. |
| | | |
| | | Debugging Imperative Authorization Failures |
| | | ------------------------------------------- |
| | | |
| | | The :meth:`pyramid.request.Request.has_permission` API is used to check |
| | | security within view functions imperatively. It returns instances of |
| | | objects that are effectively booleans. But these objects are not raw |
| | | ``True`` or ``False`` objects, and have information attached to them |
| | | about why the permission was allowed or denied. The object will be |
| | | one of :data:`pyramid.security.ACLAllowed`, |
| | | :data:`pyramid.security.ACLDenied`, |
| | | :data:`pyramid.security.Allowed`, or |
| | | :data:`pyramid.security.Denied`, as documented in |
| | | :ref:`security_module`. At the very minimum these objects will have a |
| | | ``msg`` attribute, which is a string indicating why the permission was |
| | | denied or allowed. Introspecting this information in the debugger or |
| | | via print statements when a call to |
| | | :meth:`~pyramid.request.Request.has_permission` fails is often useful. |
| | | security within view functions imperatively. It returns instances of objects |
| | | that are effectively booleans. But these objects are not raw ``True`` or |
| | | ``False`` objects, and have information attached to them about why the |
| | | permission was allowed or denied. The object will be one of |
| | | :data:`pyramid.security.ACLAllowed`, :data:`pyramid.security.ACLDenied`, |
| | | :data:`pyramid.security.Allowed`, or :data:`pyramid.security.Denied`, as |
| | | documented in :ref:`security_module`. At the very minimum, these objects will |
| | | have a ``msg`` attribute, which is a string indicating why the permission was |
| | | denied or allowed. Introspecting this information in the debugger or via print |
| | | statements when a call to :meth:`~pyramid.request.Request.has_permission` fails |
| | | is often useful. |
| | | |
| | | .. index:: |
| | | single: authentication policy (extending) |
| | |
| | | Extending Default Authentication Policies |
| | | ----------------------------------------- |
| | | |
| | | Pyramid ships with some builtin authentication policies for use in your |
| | | applications. See :mod:`pyramid.authentication` for the available |
| | | policies. They differ on their mechanisms for tracking authentication |
| | | credentials between requests, however they all interface with your |
| | | application in mostly the same way. |
| | | Pyramid ships with some built in authentication policies for use in your |
| | | applications. See :mod:`pyramid.authentication` for the available policies. |
| | | They differ on their mechanisms for tracking authentication credentials between |
| | | requests, however they all interface with your application in mostly the same |
| | | way. |
| | | |
| | | Above you learned about :ref:`assigning_acls`. Each :term:`principal` used |
| | | in the :term:`ACL` is matched against the list returned from |
| | | Above you learned about :ref:`assigning_acls`. Each :term:`principal` used in |
| | | the :term:`ACL` is matched against the list returned from |
| | | :meth:`pyramid.interfaces.IAuthenticationPolicy.effective_principals`. |
| | | Similarly, :meth:`pyramid.request.Request.authenticated_userid` maps to |
| | | :meth:`pyramid.interfaces.IAuthenticationPolicy.authenticated_userid`. |
| | | |
| | | You may control these values by subclassing the default authentication |
| | | policies. For example, below we subclass the |
| | | :class:`pyramid.authentication.AuthTktAuthenticationPolicy` and define |
| | | extra functionality to query our database before confirming that the |
| | | :term:`userid` is valid in order to avoid blindly trusting the value in the |
| | | cookie (what if the cookie is still valid but the user has deleted their |
| | | account?). We then use that :term:`userid` to augment the |
| | | ``effective_principals`` with information about groups and other state for |
| | | that user. |
| | | :class:`pyramid.authentication.AuthTktAuthenticationPolicy` and define extra |
| | | functionality to query our database before confirming that the :term:`userid` |
| | | is valid in order to avoid blindly trusting the value in the cookie (what if |
| | | the cookie is still valid, but the user has deleted their account?). We then |
| | | use that :term:`userid` to augment the ``effective_principals`` with |
| | | information about groups and other state for that user. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | return principals |
| | | |
| | | In most instances ``authenticated_userid`` and ``effective_principals`` are |
| | | application-specific whereas ``unauthenticated_userid``, ``remember`` and |
| | | ``forget`` are generic and focused on transport/serialization of data |
| | | application-specific, whereas ``unauthenticated_userid``, ``remember``, and |
| | | ``forget`` are generic and focused on transport and serialization of data |
| | | between consecutive requests. |
| | | |
| | | .. index:: |
| | |
| | | Creating Your Own Authentication Policy |
| | | --------------------------------------- |
| | | |
| | | :app:`Pyramid` ships with a number of useful out-of-the-box |
| | | security policies (see :mod:`pyramid.authentication`). However, |
| | | creating your own authentication policy is often necessary when you |
| | | want to control the "horizontal and vertical" of how your users |
| | | authenticate. Doing so is a matter of creating an instance of something |
| | | that implements the following interface: |
| | | :app:`Pyramid` ships with a number of useful out-of-the-box security policies |
| | | (see :mod:`pyramid.authentication`). However, creating your own authentication |
| | | policy is often necessary when you want to control the "horizontal and |
| | | vertical" of how your users authenticate. Doing so is a matter of creating an |
| | | instance of something that implements the following interface: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | -------------------------------------- |
| | | |
| | | An authorization policy is a policy that allows or denies access after a user |
| | | has been authenticated. Most :app:`Pyramid` applications will use the |
| | | default :class:`pyramid.authorization.ACLAuthorizationPolicy`. |
| | | has been authenticated. Most :app:`Pyramid` applications will use the default |
| | | :class:`pyramid.authorization.ACLAuthorizationPolicy`. |
| | | |
| | | However, in some cases, it's useful to be able to use a different |
| | | authorization policy than the default |
| | | :class:`~pyramid.authorization.ACLAuthorizationPolicy`. For example, it |
| | | might be desirable to construct an alternate authorization policy which |
| | | allows the application to use an authorization mechanism that does not |
| | | involve :term:`ACL` objects. |
| | | However, in some cases, it's useful to be able to use a different authorization |
| | | policy than the default :class:`~pyramid.authorization.ACLAuthorizationPolicy`. |
| | | For example, it might be desirable to construct an alternate authorization |
| | | policy which allows the application to use an authorization mechanism that does |
| | | not involve :term:`ACL` objects. |
| | | |
| | | :app:`Pyramid` ships with only a single default authorization |
| | | policy, so you'll need to create your own if you'd like to use a |
| | | different one. Creating and using your own authorization policy is a |
| | | matter of creating an instance of an object that implements the |
| | | following interface: |
| | | :app:`Pyramid` ships with only a single default authorization policy, so you'll |
| | | need to create your own if you'd like to use a different one. Creating and |
| | | using your own authorization policy is a matter of creating an instance of an |
| | | object that implements the following interface: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | a secret across two different subsystems might drop the security of signing to |
| | | zero. Keys should not be re-used across different contexts where an attacker |
| | | has the possibility of providing a chosen plaintext. |
| | | |
| | |
| | | When you cause a :app:`Pyramid` application to start up in a console window, |
| | | you'll see something much like this show up on the console: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ pserve development.ini |
| | | Starting server in PID 16601. |
| | | serving on 0.0.0.0:6543 view at http://127.0.0.1:6543 |
| | | $ $VENV/bin/pserve development.ini |
| | | Starting server in PID 16305. |
| | | serving on http://127.0.0.1:6543 |
| | | |
| | | This chapter explains what happens between the time you press the "Return" key |
| | | on your keyboard after typing ``pserve development.ini`` and the time the line |
| | | ``serving on 0.0.0.0:6543 ...`` is output to your console. |
| | | ``serving on http://127.0.0.1:6543`` is output to your console. |
| | | |
| | | .. index:: |
| | | single: startup process |
| | |
| | | In this case, the ``myproject.__init__:main`` function referred to by the |
| | | entry point URI ``egg:MyProject`` (see :ref:`MyProject_ini` for more |
| | | information about entry point URIs, and how they relate to callables) will |
| | | receive the key/value pairs ``{'pyramid.reload_templates':'true', |
| | | 'pyramid.debug_authorization':'false', 'pyramid.debug_notfound':'false', |
| | | 'pyramid.debug_routematch':'false', 'pyramid.debug_templates':'true', |
| | | 'pyramid.default_locale_name':'en'}``. See :ref:`environment_chapter` for |
| | | the meanings of these keys. |
| | | receive the key/value pairs ``{pyramid.reload_templates = true, |
| | | pyramid.debug_authorization = false, pyramid.debug_notfound = false, |
| | | pyramid.debug_routematch = false, pyramid.default_locale_name = en, and |
| | | pyramid.includes = pyramid_debugtoolbar}``. See :ref:`environment_chapter` |
| | | for the meanings of these keys. |
| | | |
| | | #. The ``main`` function first constructs a |
| | | :class:`~pyramid.config.Configurator` instance, passing the ``settings`` |
| | |
| | | #. ``pserve`` starts the WSGI *server* defined within the ``[server:main]`` |
| | | section. In our case, this is the Waitress server (``use = |
| | | egg:waitress#main``), and it will listen on all interfaces (``host = |
| | | 0.0.0.0``), on port number 6543 (``port = 6543``). The server code itself |
| | | is what prints ``serving on 0.0.0.0:6543 view at http://127.0.0.1:6543``. |
| | | The server serves the application, and the application is running, waiting |
| | | to receive requests. |
| | | 127.0.0.1``), on port number 6543 (``port = 6543``). The server code itself |
| | | is what prints ``serving on http://127.0.0.1:6543``. The server serves the |
| | | application, and the application is running, waiting to receive requests. |
| | | |
| | | .. seealso:: |
| | | Logging configuration is described in the :ref:`logging_chapter` chapter. |
| | |
| | | Here's an example application which uses a subrequest: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from wsgiref.simple_server import make_server |
| | | from pyramid.config import Configurator |
| | |
| | | server = make_server('0.0.0.0', 8080, app) |
| | | server.serve_forever() |
| | | |
| | | When ``/view_one`` is visted in a browser, the text printed in the browser |
| | | pane will be ``This came from view_two``. The ``view_one`` view used the |
| | | :meth:`pyramid.request.Request.invoke_subrequest` API to obtain a response |
| | | from another view (``view_two``) within the same application when it |
| | | executed. It did so by constructing a new request that had a URL that it |
| | | knew would match the ``view_two`` view registration, and passed that new |
| | | request along to :meth:`pyramid.request.Request.invoke_subrequest`. The |
| | | ``view_two`` view callable was invoked, and it returned a response. The |
| | | ``view_one`` view callable then simply returned the response it obtained from |
| | | the ``view_two`` view callable. |
| | | When ``/view_one`` is visted in a browser, the text printed in the browser pane |
| | | will be ``This came from view_two``. The ``view_one`` view used the |
| | | :meth:`pyramid.request.Request.invoke_subrequest` API to obtain a response from |
| | | another view (``view_two``) within the same application when it executed. It |
| | | did so by constructing a new request that had a URL that it knew would match |
| | | the ``view_two`` view registration, and passed that new request along to |
| | | :meth:`pyramid.request.Request.invoke_subrequest`. The ``view_two`` view |
| | | callable was invoked, and it returned a response. The ``view_one`` view |
| | | callable then simply returned the response it obtained from the ``view_two`` |
| | | view callable. |
| | | |
| | | Note that it doesn't matter if the view callable invoked via a subrequest |
| | | actually returns a *literal* Response object. Any view callable that uses a |
| | |
| | | object: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | :emphasize-lines: 11 |
| | | |
| | | from wsgiref.simple_server import make_server |
| | | from pyramid.config import Configurator |
| | |
| | | server = make_server('0.0.0.0', 8080, app) |
| | | server.serve_forever() |
| | | |
| | | Even though the ``view_two`` view callable returned a string, it was invoked |
| | | in such a way that the ``string`` renderer associated with the view |
| | | registration that was found turned it into a "real" response object for |
| | | consumption by ``view_one``. |
| | | Even though the ``view_two`` view callable returned a string, it was invoked in |
| | | such a way that the ``string`` renderer associated with the view registration |
| | | that was found turned it into a "real" response object for consumption by |
| | | ``view_one``. |
| | | |
| | | Being able to unconditionally obtain a response object by invoking a view |
| | | callable indirectly is the main advantage to using |
| | | :meth:`pyramid.request.Request.invoke_subrequest` instead of simply importing |
| | | a view callable and executing it directly. Note that there's not much |
| | | advantage to invoking a view using a subrequest if you *can* invoke a view |
| | | callable directly. Subrequests are slower and are less convenient if you |
| | | actually do want just the literal information returned by a function that |
| | | happens to be a view callable. |
| | | :meth:`pyramid.request.Request.invoke_subrequest` instead of simply importing a |
| | | view callable and executing it directly. Note that there's not much advantage |
| | | to invoking a view using a subrequest if you *can* invoke a view callable |
| | | directly. Subrequests are slower and are less convenient if you actually do |
| | | want just the literal information returned by a function that happens to be a |
| | | view callable. |
| | | |
| | | Note that, by default, if a view callable invoked by a subrequest raises an |
| | | exception, the exception will be raised to the caller of |
| | |
| | | :term:`exception view` configured: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | :emphasize-lines: 11-16 |
| | | |
| | | from wsgiref.simple_server import make_server |
| | | from pyramid.config import Configurator |
| | |
| | | ``excview`` :term:`exception view` will *not* be executed. Instead, the call |
| | | to :meth:`~pyramid.request.Request.invoke_subrequest` will cause a |
| | | :exc:`ValueError` exception to be raised and a response will never be |
| | | generated. We can change this behavior; how to do so is described below in |
| | | our discussion of the ``use_tweens`` argument. |
| | | generated. We can change this behavior; how to do so is described below in our |
| | | discussion of the ``use_tweens`` argument. |
| | | |
| | | .. index:: |
| | | pair: subrequest; use_tweens |
| | | |
| | | Subrequests with Tweens |
| | | ----------------------- |
| | | |
| | | The :meth:`pyramid.request.Request.invoke_subrequest` API accepts two |
| | | arguments: a positional argument ``request`` that must be provided, and |
| | | ``use_tweens`` keyword argument that is optional; it defaults to ``False``. |
| | | arguments: a required positional argument ``request``, and an optional keyword |
| | | argument ``use_tweens`` which defaults to ``False``. |
| | | |
| | | The ``request`` object passed to the API must be an object that implements |
| | | the Pyramid request interface (such as a :class:`pyramid.request.Request` |
| | | The ``request`` object passed to the API must be an object that implements the |
| | | Pyramid request interface (such as a :class:`pyramid.request.Request` |
| | | instance). If ``use_tweens`` is ``True``, the request will be sent to the |
| | | :term:`tween` in the tween stack closest to the request ingress. If |
| | | ``use_tweens`` is ``False``, the request will be sent to the main router |
| | |
| | | In the example above, the call to |
| | | :meth:`~pyramid.request.Request.invoke_subrequest` will always raise an |
| | | exception. This is because it's using the default value for ``use_tweens``, |
| | | which is ``False``. You can pass ``use_tweens=True`` instead to ensure that |
| | | it will convert an exception to a Response if an :term:`exception view` is |
| | | configured instead of raising the exception. This is because exception views |
| | | which is ``False``. Alternatively, you can pass ``use_tweens=True`` to ensure |
| | | that it will convert an exception to a Response if an :term:`exception view` is |
| | | configured, instead of raising the exception. This is because exception views |
| | | are called by the exception view :term:`tween` as described in |
| | | :ref:`exception_views` when any view raises an exception. |
| | | |
| | |
| | | :meth:`~pyramid.request.Request.invoke_subrequest`, like this: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | :emphasize-lines: 7 |
| | | |
| | | from wsgiref.simple_server import make_server |
| | | from pyramid.config import Configurator |
| | |
| | | exception view to generate a response is run, and therefore ``excview`` is |
| | | executed. |
| | | |
| | | This is one of the major differences between specifying the |
| | | ``use_tweens=True`` and ``use_tweens=False`` arguments to |
| | | This is one of the major differences between specifying the ``use_tweens=True`` |
| | | and ``use_tweens=False`` arguments to |
| | | :meth:`~pyramid.request.Request.invoke_subrequest`. ``use_tweens=True`` may |
| | | also imply invoking transaction commit/abort for the logic executed in the |
| | | subrequest if you've got ``pyramid_tm`` in the tween list, injecting debug |
| | | HTML if you've got ``pyramid_debugtoolbar`` in the tween list, and other |
| | | also imply invoking a transaction commit or abort for the logic executed in the |
| | | subrequest if you've got ``pyramid_tm`` in the tween list, injecting debug HTML |
| | | if you've got ``pyramid_debugtoolbar`` in the tween list, and other |
| | | tween-related side effects as defined by your particular tween list. |
| | | |
| | | The :meth:`~pyramid.request.Request.invoke_subrequest` function also |
| | | unconditionally: |
| | | |
| | | - manages the threadlocal stack so that |
| | | unconditionally does the following: |
| | | |
| | | - It manages the threadlocal stack so that |
| | | :func:`~pyramid.threadlocal.get_current_request` and |
| | | :func:`~pyramid.threadlocal.get_current_registry` work during a request |
| | | (they will return the subrequest instead of the original request) |
| | | :func:`~pyramid.threadlocal.get_current_registry` work during a request (they |
| | | will return the subrequest instead of the original request). |
| | | |
| | | - Adds a ``registry`` attribute and a ``invoke_subrequest`` attribute (a |
| | | callable) to the request object it's handed. |
| | | - It adds a ``registry`` attribute and an ``invoke_subrequest`` attribute (a |
| | | callable) to the request object to which it is handed. |
| | | |
| | | - sets request extensions (such as those added via |
| | | - It sets request extensions (such as those added via |
| | | :meth:`~pyramid.config.Configurator.add_request_method` or |
| | | :meth:`~pyramid.config.Configurator.set_request_property`) on the subrequest |
| | | object passed as ``request`` |
| | | object passed as ``request``. |
| | | |
| | | - causes a :class:`~pyramid.events.NewRequest` event to be sent at the |
| | | - It causes a :class:`~pyramid.events.NewRequest` event to be sent at the |
| | | beginning of request processing. |
| | | |
| | | - causes a :class:`~pyramid.events.ContextFound` event to be sent when a |
| | | - It causes a :class:`~pyramid.events.ContextFound` event to be sent when a |
| | | context resource is found. |
| | | |
| | | - Ensures that the user implied by the request passed has the necessary |
| | | authorization to invoke view callable before calling it. |
| | | - It ensures that the user implied by the request passed in has the necessary |
| | | authorization to invoke the view callable before calling it. |
| | | |
| | | - Calls any :term:`response callback` functions defined within the subrequest's |
| | | lifetime if a response is obtained from the Pyramid application. |
| | | - It calls any :term:`response callback` functions defined within the |
| | | subrequest's lifetime if a response is obtained from the Pyramid application. |
| | | |
| | | - causes a :class:`~pyramid.events.NewResponse` event to be sent if a response |
| | | is obtained. |
| | | - It causes a :class:`~pyramid.events.NewResponse` event to be sent if a |
| | | response is obtained. |
| | | |
| | | - Calls any :term:`finished callback` functions defined within the subrequest's |
| | | lifetime. |
| | | - It calls any :term:`finished callback` functions defined within the |
| | | subrequest's lifetime. |
| | | |
| | | The invocation of a subrequest has more or less exactly the same effect as |
| | | the invocation of a request received by the Pyramid router from a web client |
| | | The invocation of a subrequest has more or less exactly the same effect as the |
| | | invocation of a request received by the :app:`Pyramid` router from a web client |
| | | when ``use_tweens=True``. When ``use_tweens=False``, the tweens are skipped |
| | | but all the other steps take place. |
| | | |
| | | It's a poor idea to use the original ``request`` object as an argument to |
| | | :meth:`~pyramid.request.Request.invoke_subrequest`. You should construct a |
| | | new request instead as demonstrated in the above example, using |
| | | :meth:`~pyramid.request.Request.invoke_subrequest`. You should construct a new |
| | | request instead as demonstrated in the above example, using |
| | | :meth:`pyramid.request.Request.blank`. Once you've constructed a request |
| | | object, you'll need to massage it to match the view callable you'd like |
| | | to be executed during the subrequest. This can be done by adjusting the |
| | | object, you'll need to massage it to match the view callable that you'd like to |
| | | be executed during the subrequest. This can be done by adjusting the |
| | | subrequest's URL, its headers, its request method, and other attributes. The |
| | | documentation for :class:`pyramid.request.Request` exposes the methods you |
| | | should call and attributes you should set on the request you create to |
| | | massage it into something that will actually match the view you'd like to |
| | | call via a subrequest. |
| | | should call and attributes you should set on the request that you create, then |
| | | massage it into something that will actually match the view you'd like to call |
| | | via a subrequest. |
| | | |
| | | We've demonstrated use of a subrequest from within a view callable, but you |
| | | can use the :meth:`~pyramid.request.Request.invoke_subrequest` API from |
| | | within a tween or an event handler as well. It's usually a poor idea to |
| | | invoke :meth:`~pyramid.request.Request.invoke_subrequest` from within a |
| | | tween, because tweens already by definition have access to a function that |
| | | will cause a subrequest (they are passed a ``handle`` function), but you can |
| | | do it. It's fine to invoke |
| | | :meth:`~pyramid.request.Request.invoke_subrequest` from within an event |
| | | handler, however. |
| | | We've demonstrated use of a subrequest from within a view callable, but you can |
| | | use the :meth:`~pyramid.request.Request.invoke_subrequest` API from within a |
| | | tween or an event handler as well. Even though you can do it, it's usually a |
| | | poor idea to invoke :meth:`~pyramid.request.Request.invoke_subrequest` from |
| | | within a tween, because tweens already, by definition, have access to a |
| | | function that will cause a subrequest (they are passed a ``handle`` function). |
| | | It's fine to invoke :meth:`~pyramid.request.Request.invoke_subrequest` from |
| | | within an event handler, however. |
| | |
| | | class instance. The unit is also referred to as a "unit under test". |
| | | |
| | | The goal of a single unit test is to test **only** some permutation of the |
| | | "unit under test". If you write a unit test that aims to verify the result |
| | | of a particular codepath through a Python function, you need only be |
| | | concerned about testing the code that *lives in the function body itself*. |
| | | If the function accepts a parameter that represents a complex application |
| | | "domain object" (such as a resource, a database connection, or an SMTP |
| | | server), the argument provided to this function during a unit test *need not |
| | | be* and likely *should not be* a "real" implementation object. For example, |
| | | although a particular function implementation may accept an argument that |
| | | represents an SMTP server object, and the function may call a method of this |
| | | object when the system is operating normally that would result in an email |
| | | being sent, a unit test of this codepath of the function does *not* need to |
| | | test that an email is actually sent. It just needs to make sure that the |
| | | function calls the method of the object provided as an argument that *would* |
| | | send an email if the argument happened to be the "real" implementation of an |
| | | SMTP server object. |
| | | "unit under test". If you write a unit test that aims to verify the result of |
| | | a particular codepath through a Python function, you need only be concerned |
| | | about testing the code that *lives in the function body itself*. If the |
| | | function accepts a parameter that represents a complex application "domain |
| | | object" (such as a resource, a database connection, or an SMTP server), the |
| | | argument provided to this function during a unit test *need not be* and likely |
| | | *should not be* a "real" implementation object. For example, although a |
| | | particular function implementation may accept an argument that represents an |
| | | SMTP server object, and the function may call a method of this object when the |
| | | system is operating normally that would result in an email being sent, a unit |
| | | test of this codepath of the function does *not* need to test that an email is |
| | | actually sent. It just needs to make sure that the function calls the method |
| | | of the object provided as an argument that *would* send an email if the |
| | | argument happened to be the "real" implementation of an SMTP server object. |
| | | |
| | | An *integration test*, on the other hand, is a different form of testing in |
| | | which the interaction between two or more "units" is explicitly tested. |
| | | Integration tests verify that the components of your application work |
| | | together. You *might* make sure that an email was actually sent in an |
| | | integration test. |
| | | Integration tests verify that the components of your application work together. |
| | | You *might* make sure that an email was actually sent in an integration test. |
| | | |
| | | A *functional test* is a form of integration test in which the application is |
| | | run "literally". You would *have to* make sure that an email was actually |
| | | sent in a functional test, because it tests your code end to end. |
| | | run "literally". You would *have to* make sure that an email was actually sent |
| | | in a functional test, because it tests your code end to end. |
| | | |
| | | It is often considered best practice to write each type of tests for any |
| | | given codebase. Unit testing often provides the opportunity to obtain better |
| | | It is often considered best practice to write each type of tests for any given |
| | | codebase. Unit testing often provides the opportunity to obtain better |
| | | "coverage": it's usually possible to supply a unit under test with arguments |
| | | and/or an environment which causes *all* of its potential codepaths to be |
| | | executed. This is usually not as easy to do with a set of integration or |
| | |
| | | Into Python <http://www.diveintopython.net/unit_testing/index.html>`_ by Mark |
| | | Pilgrim. |
| | | |
| | | :app:`Pyramid` provides a number of facilities that make unit, integration, |
| | | and functional tests easier to write. The facilities become particularly |
| | | useful when your code calls into :app:`Pyramid` -related framework functions. |
| | | :app:`Pyramid` provides a number of facilities that make unit, integration, and |
| | | functional tests easier to write. The facilities become particularly useful |
| | | when your code calls into :app:`Pyramid`-related framework functions. |
| | | |
| | | .. index:: |
| | | single: test setup |
| | |
| | | .. _test_setup_and_teardown: |
| | | |
| | | Test Set Up and Tear Down |
| | | -------------------------- |
| | | ------------------------- |
| | | |
| | | :app:`Pyramid` uses a "global" (actually :term:`thread local`) data structure |
| | | to hold two items: the current :term:`request` and the current |
| | | :term:`application registry`. These data structures are available via the |
| | | :func:`pyramid.threadlocal.get_current_request` and |
| | | :func:`pyramid.threadlocal.get_current_registry` functions, respectively. |
| | | See :ref:`threadlocals_chapter` for information about these functions and the |
| | | data structures they return. |
| | | :func:`pyramid.threadlocal.get_current_registry` functions, respectively. See |
| | | :ref:`threadlocals_chapter` for information about these functions and the data |
| | | structures they return. |
| | | |
| | | If your code uses these ``get_current_*`` functions or calls :app:`Pyramid` |
| | | code which uses ``get_current_*`` functions, you will need to call |
| | | :func:`pyramid.testing.setUp` in your test setup and you will need to call |
| | | :func:`pyramid.testing.tearDown` in your test teardown. |
| | | :func:`~pyramid.testing.setUp` pushes a registry onto the :term:`thread |
| | | local` stack, which makes the ``get_current_*`` functions work. It returns a |
| | | :func:`~pyramid.testing.setUp` pushes a registry onto the :term:`thread local` |
| | | stack, which makes the ``get_current_*`` functions work. It returns a |
| | | :term:`Configurator` object which can be used to perform extra configuration |
| | | required by the code under test. :func:`~pyramid.testing.tearDown` pops the |
| | | thread local stack. |
| | | |
| | | Normally when a Configurator is used directly with the ``main`` block of |
| | | a Pyramid application, it defers performing any "real work" until its |
| | | ``.commit`` method is called (often implicitly by the |
| | | :meth:`pyramid.config.Configurator.make_wsgi_app` method). The |
| | | Configurator returned by :func:`~pyramid.testing.setUp` is an |
| | | *autocommitting* Configurator, however, which performs all actions |
| | | implied by methods called on it immediately. This is more convenient |
| | | for unit-testing purposes than needing to call |
| | | :meth:`pyramid.config.Configurator.commit` in each test after adding |
| | | extra configuration statements. |
| | | Normally when a Configurator is used directly with the ``main`` block of a |
| | | Pyramid application, it defers performing any "real work" until its ``.commit`` |
| | | method is called (often implicitly by the |
| | | :meth:`pyramid.config.Configurator.make_wsgi_app` method). The Configurator |
| | | returned by :func:`~pyramid.testing.setUp` is an *autocommitting* Configurator, |
| | | however, which performs all actions implied by methods called on it |
| | | immediately. This is more convenient for unit testing purposes than needing to |
| | | call :meth:`pyramid.config.Configurator.commit` in each test after adding extra |
| | | configuration statements. |
| | | |
| | | The use of the :func:`~pyramid.testing.setUp` and |
| | | :func:`~pyramid.testing.tearDown` functions allows you to supply each unit |
| | | test method in a test case with an environment that has an isolated registry |
| | | and an isolated request for the duration of a single test. Here's an example |
| | | of using this feature: |
| | | :func:`~pyramid.testing.tearDown` functions allows you to supply each unit test |
| | | method in a test case with an environment that has an isolated registry and an |
| | | isolated request for the duration of a single test. Here's an example of using |
| | | this feature: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | def tearDown(self): |
| | | testing.tearDown() |
| | | |
| | | The above will make sure that |
| | | :func:`~pyramid.threadlocal.get_current_registry` called within a test |
| | | case method of ``MyTest`` will return the :term:`application registry` |
| | | associated with the ``config`` Configurator instance. Each test case |
| | | method attached to ``MyTest`` will use an isolated registry. |
| | | The above will make sure that :func:`~pyramid.threadlocal.get_current_registry` |
| | | called within a test case method of ``MyTest`` will return the |
| | | :term:`application registry` associated with the ``config`` Configurator |
| | | instance. Each test case method attached to ``MyTest`` will use an isolated |
| | | registry. |
| | | |
| | | The :func:`~pyramid.testing.setUp` and :func:`~pyramid.testing.tearDown` |
| | | functions accepts various arguments that influence the environment of the |
| | | test. See the :ref:`testing_module` API for information about the extra |
| | | arguments supported by these functions. |
| | | functions accept various arguments that influence the environment of the test. |
| | | See the :ref:`testing_module` API for information about the extra arguments |
| | | supported by these functions. |
| | | |
| | | If you also want to make :func:`~pyramid.threadlocal.get_current_request` |
| | | return something other than ``None`` during the course of a single test, you |
| | | can pass a |
| | | :term:`request` object into the :func:`pyramid.testing.setUp` within the |
| | | ``setUp`` method of your test: |
| | | can pass a :term:`request` object into the :func:`pyramid.testing.setUp` within |
| | | the ``setUp`` method of your test: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | def tearDown(self): |
| | | testing.tearDown() |
| | | |
| | | If you pass a :term:`request` object into :func:`pyramid.testing.setUp` |
| | | within your test case's ``setUp``, any test method attached to the |
| | | ``MyTest`` test case that directly or indirectly calls |
| | | If you pass a :term:`request` object into :func:`pyramid.testing.setUp` within |
| | | your test case's ``setUp``, any test method attached to the ``MyTest`` test |
| | | case that directly or indirectly calls |
| | | :func:`~pyramid.threadlocal.get_current_request` will receive the request |
| | | object. Otherwise, during testing, |
| | | :func:`~pyramid.threadlocal.get_current_request` will return ``None``. |
| | | We use a "dummy" request implementation supplied by |
| | | :class:`pyramid.testing.DummyRequest` because it's easier to construct |
| | | than a "real" :app:`Pyramid` request object. |
| | | :func:`~pyramid.threadlocal.get_current_request` will return ``None``. We use a |
| | | "dummy" request implementation supplied by |
| | | :class:`pyramid.testing.DummyRequest` because it's easier to construct than a |
| | | "real" :app:`Pyramid` request object. |
| | | |
| | | Test setup using a context manager |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | An alternative style of setting up a test configuration is to use the |
| | | `with` statement and :func:`pyramid.testing.testConfig` to create a |
| | | context manager. The context manager will call |
| | | :func:`pyramid.testing.setUp` before the code under test and |
| | | :func:`pyramid.testing.tearDown` afterwards. |
| | | An alternative style of setting up a test configuration is to use the ``with`` |
| | | statement and :func:`pyramid.testing.testConfig` to create a context manager. |
| | | The context manager will call :func:`pyramid.testing.setUp` before the code |
| | | under test and :func:`pyramid.testing.tearDown` afterwards. |
| | | |
| | | This style is useful for small self-contained tests. For example: |
| | | |
| | |
| | | about any of this, but you still want to write test code, just always call |
| | | :func:`pyramid.testing.setUp` in your test's ``setUp`` method and |
| | | :func:`pyramid.testing.tearDown` in your tests' ``tearDown`` method. This |
| | | won't really hurt anything if the application you're testing does not call |
| | | any ``get_current*`` function. |
| | | won't really hurt anything if the application you're testing does not call any |
| | | ``get_current*`` function. |
| | | |
| | | .. index:: |
| | | single: pyramid.testing |
| | |
| | | .. note:: |
| | | |
| | | This code implies that you have defined a renderer imperatively in a |
| | | relevant :class:`pyramid.config.Configurator` instance, |
| | | otherwise it would fail when run normally. |
| | | relevant :class:`pyramid.config.Configurator` instance, otherwise it would |
| | | fail when run normally. |
| | | |
| | | Without doing anything special during a unit test, the call to |
| | | :meth:`~pyramid.request.Request.has_permission` in this view function will |
| | | always return a ``True`` value. When a :app:`Pyramid` application starts |
| | | normally, it will populate a :term:`application registry` using |
| | | normally, it will populate an :term:`application registry` using |
| | | :term:`configuration declaration` calls made against a :term:`Configurator`. |
| | | But if this application registry is not created and populated (e.g. by |
| | | But if this application registry is not created and populated (e.g., by |
| | | initializing the configurator with an authorization policy), like when you |
| | | invoke application code via a unit test, :app:`Pyramid` API functions will tend |
| | | to either fail or return default results. So how do you test the branch of the |
| | |
| | | be found when ``setup.py test`` is run. It has two test methods. |
| | | |
| | | The first test method, ``test_view_fn_forbidden`` tests the ``view_fn`` when |
| | | the authentication policy forbids the current user the ``edit`` permission. |
| | | Its third line registers a "dummy" "non-permissive" authorization policy |
| | | using the :meth:`~pyramid.config.Configurator.testing_securitypolicy` method, |
| | | which is a special helper method for unit testing. |
| | | the authentication policy forbids the current user the ``edit`` permission. Its |
| | | third line registers a "dummy" "non-permissive" authorization policy using the |
| | | :meth:`~pyramid.config.Configurator.testing_securitypolicy` method, which is a |
| | | special helper method for unit testing. |
| | | |
| | | We then create a :class:`pyramid.testing.DummyRequest` object which simulates a |
| | | WebOb request object API. A :class:`pyramid.testing.DummyRequest` is a request |
| | |
| | | |
| | | The second test method, named ``test_view_fn_allowed``, tests the alternate |
| | | case, where the authentication policy allows access. Notice that we pass |
| | | different values to |
| | | :meth:`~pyramid.config.Configurator.testing_securitypolicy` to obtain this |
| | | result. We assert at the end of this that the view function returns a value. |
| | | different values to :meth:`~pyramid.config.Configurator.testing_securitypolicy` |
| | | to obtain this result. We assert at the end of this that the view function |
| | | returns a value. |
| | | |
| | | Note that the test calls the :func:`pyramid.testing.setUp` function in its |
| | | ``setUp`` method and the :func:`pyramid.testing.tearDown` function in its |
| | | ``tearDown`` method. We assign the result of :func:`pyramid.testing.setUp` |
| | | as ``config`` on the unittest class. This is a :term:`Configurator` object |
| | | and all methods of the configurator can be called as necessary within |
| | | tests. If you use any of the :class:`~pyramid.config.Configurator` APIs during |
| | | testing, be sure to use this pattern in your test case's ``setUp`` and |
| | | ``tearDown``; these methods make sure you're using a "fresh" |
| | | :term:`application registry` per test run. |
| | | ``tearDown`` method. We assign the result of :func:`pyramid.testing.setUp` as |
| | | ``config`` on the unittest class. This is a :term:`Configurator` object and |
| | | all methods of the configurator can be called as necessary within tests. If you |
| | | use any of the :class:`~pyramid.config.Configurator` APIs during testing, be |
| | | sure to use this pattern in your test case's ``setUp`` and ``tearDown``; these |
| | | methods make sure you're using a "fresh" :term:`application registry` per test |
| | | run. |
| | | |
| | | See the :ref:`testing_module` chapter for the entire :app:`Pyramid` -specific |
| | | See the :ref:`testing_module` chapter for the entire :app:`Pyramid`-specific |
| | | testing API. This chapter describes APIs for registering a security policy, |
| | | registering resources at paths, registering event listeners, registering |
| | | views and view permissions, and classes representing "dummy" implementations |
| | | of a request and a resource. |
| | | registering resources at paths, registering event listeners, registering views |
| | | and view permissions, and classes representing "dummy" implementations of a |
| | | request and a resource. |
| | | |
| | | .. seealso:: |
| | | |
| | |
| | | |
| | | See also :ref:`including_configuration` |
| | | |
| | | Let's demonstrate this by showing an integration test for a view. |
| | | |
| | | Given the following view definition, which assumes that your application's |
| | | :term:`package` name is ``myproject``, and within that :term:`package` there |
| | | exists a module ``views``, which in turn contains a :term:`view` function named |
| | | ``my_view``: |
| | | |
| | | .. literalinclude:: MyProject/myproject/views.py |
| | | :linenos: |
| | | :lines: 1-6 |
| | | :language: python |
| | | |
| | | You'd then create a ``tests`` module within your ``myproject`` package, |
| | | containing the following test code: |
| | | |
| | | .. literalinclude:: MyProject/myproject/tests.py |
| | | :linenos: |
| | | :pyobject: ViewIntegrationTests |
| | | :language: python |
| | | |
| | | Writing unit tests that use the :class:`~pyramid.config.Configurator` API to |
| | | set up the right "mock" registrations is often preferred to creating |
| | | integration tests. Unit tests will run faster (because they do less for each |
| | |
| | | package, which provides APIs for invoking HTTP(S) requests to your application. |
| | | |
| | | Regardless of which testing :term:`package` you use, ensure to add a |
| | | ``tests_require`` dependency on that package to to your application's |
| | | ``setup.py`` file: |
| | | ``tests_require`` dependency on that package to your application's |
| | | ``setup.py`` file. Using the project ``MyProject`` generated by the starter |
| | | scaffold as described in :doc:`project`, we would insert the following code immediately following the |
| | | ``requires`` block in the file ``MyProject/setup.py``. |
| | | |
| | | .. literalinclude:: MyProject/setup.py |
| | | :linenos: |
| | | :emphasize-lines: 26-28,48 |
| | | :language: python |
| | | .. code-block:: ini |
| | | :linenos: |
| | | :lineno-start: 11 |
| | | :emphasize-lines: 8- |
| | | |
| | | Assuming your :term:`package` is named ``myproject``, which contains a |
| | | ``views`` module, which in turn contains a :term:`view` function ``my_view`` |
| | | that returns a HTML body when the root URL is invoked: |
| | | requires = [ |
| | | 'pyramid', |
| | | 'pyramid_chameleon', |
| | | 'pyramid_debugtoolbar', |
| | | 'waitress', |
| | | ] |
| | | |
| | | test_requires = [ |
| | | 'webtest', |
| | | ] |
| | | |
| | | Remember to change the dependency. |
| | | |
| | | .. code-block:: ini |
| | | :linenos: |
| | | :lineno-start: 39 |
| | | :emphasize-lines: 2 |
| | | |
| | | install_requires=requires, |
| | | tests_require=test_requires, |
| | | test_suite="myproject", |
| | | |
| | | As always, whenever you change your dependencies, make sure to run the |
| | | following command. |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $VENV/bin/python setup.py develop |
| | | |
| | | In your ``MyPackage`` project, your :term:`package` is named ``myproject`` |
| | | which contains a ``views`` module, which in turn contains a :term:`view` |
| | | function ``my_view`` that returns an HTML body when the root URL is invoked: |
| | | |
| | | .. literalinclude:: MyProject/myproject/views.py |
| | | :linenos: |
| | | :language: python |
| | | |
| | | Then the following example functional test (shown below) demonstrates invoking |
| | | the :term:`view` shown above: |
| | | The following example functional test demonstrates invoking the above |
| | | :term:`view`: |
| | | |
| | | .. literalinclude:: MyProject/myproject/tests.py |
| | | :linenos: |
| | |
| | | When this test is run, each test method creates a "real" :term:`WSGI` |
| | | application using the ``main`` function in your ``myproject.__init__`` module, |
| | | using :term:`WebTest` to wrap that WSGI application. It assigns the result to |
| | | ``self.testapp``. In the test named ``test_root``. The ``TestApp``'s ``get`` |
| | | ``self.testapp``. In the test named ``test_root``, the ``TestApp``'s ``GET`` |
| | | method is used to invoke the root URL. Finally, an assertion is made that the |
| | | returned HTML contains the text ``MyProject``. |
| | | returned HTML contains the text ``Pyramid``. |
| | | |
| | | See the :term:`WebTest` documentation for further information about the |
| | | methods available to a :class:`webtest.app.TestApp` instance. |
| | | See the :term:`WebTest` documentation for further information about the methods |
| | | available to a :class:`webtest.app.TestApp` instance. |
| | |
| | | Thread Locals |
| | | ============= |
| | | |
| | | A :term:`thread local` variable is a variable that appears to be a |
| | | "global" variable to an application which uses it. However, unlike a |
| | | true global variable, one thread or process serving the application |
| | | may receive a different value than another thread or process when that |
| | | variable is "thread local". |
| | | A :term:`thread local` variable is a variable that appears to be a "global" |
| | | variable to an application which uses it. However, unlike a true global |
| | | variable, one thread or process serving the application may receive a different |
| | | value than another thread or process when that variable is "thread local". |
| | | |
| | | When a request is processed, :app:`Pyramid` makes two :term:`thread |
| | | local` variables available to the application: a "registry" and a |
| | | "request". |
| | | When a request is processed, :app:`Pyramid` makes two :term:`thread local` |
| | | variables available to the application: a "registry" and a "request". |
| | | |
| | | Why and How :app:`Pyramid` Uses Thread Local Variables |
| | | --------------------------------------------------------- |
| | | ------------------------------------------------------ |
| | | |
| | | How are thread locals beneficial to :app:`Pyramid` and application |
| | | developers who use :app:`Pyramid`? Well, usually they're decidedly |
| | | **not**. Using a global or a thread local variable in any application |
| | | usually makes it a lot harder to understand for a casual reader. Use |
| | | of a thread local or a global is usually just a way to avoid passing |
| | | some value around between functions, which is itself usually a very |
| | | bad idea, at least if code readability counts as an important concern. |
| | | How are thread locals beneficial to :app:`Pyramid` and application developers |
| | | who use :app:`Pyramid`? Well, usually they're decidedly **not**. Using a |
| | | global or a thread local variable in any application usually makes it a lot |
| | | harder to understand for a casual reader. Use of a thread local or a global is |
| | | usually just a way to avoid passing some value around between functions, which |
| | | is itself usually a very bad idea, at least if code readability counts as an |
| | | important concern. |
| | | |
| | | For historical reasons, however, thread local variables are indeed consulted by |
| | | various :app:`Pyramid` API functions. For example, the implementation of the |
| | |
| | | uses the authentication policy to retrieve the authenticated user id. This is |
| | | how :app:`Pyramid` allows arbitrary authentication policies to be "plugged in". |
| | | |
| | | When they need to do so, :app:`Pyramid` internals use two API |
| | | functions to retrieve the :term:`request` and :term:`application |
| | | registry`: :func:`~pyramid.threadlocal.get_current_request` and |
| | | :func:`~pyramid.threadlocal.get_current_registry`. The former |
| | | returns the "current" request; the latter returns the "current" |
| | | registry. Both ``get_current_*`` functions retrieve an object from a |
| | | thread-local data structure. These API functions are documented in |
| | | :ref:`threadlocal_module`. |
| | | When they need to do so, :app:`Pyramid` internals use two API functions to |
| | | retrieve the :term:`request` and :term:`application registry`: |
| | | :func:`~pyramid.threadlocal.get_current_request` and |
| | | :func:`~pyramid.threadlocal.get_current_registry`. The former returns the |
| | | "current" request; the latter returns the "current" registry. Both |
| | | ``get_current_*`` functions retrieve an object from a thread-local data |
| | | structure. These API functions are documented in :ref:`threadlocal_module`. |
| | | |
| | | These values are thread locals rather than true globals because one |
| | | Python process may be handling multiple simultaneous requests or even |
| | | multiple :app:`Pyramid` applications. If they were true globals, |
| | | :app:`Pyramid` could not handle multiple simultaneous requests or |
| | | allow more than one :app:`Pyramid` application instance to exist in |
| | | a single Python process. |
| | | These values are thread locals rather than true globals because one Python |
| | | process may be handling multiple simultaneous requests or even multiple |
| | | :app:`Pyramid` applications. If they were true globals, :app:`Pyramid` could |
| | | not handle multiple simultaneous requests or allow more than one :app:`Pyramid` |
| | | application instance to exist in a single Python process. |
| | | |
| | | Because one :app:`Pyramid` application is permitted to call |
| | | *another* :app:`Pyramid` application from its own :term:`view` code |
| | | (perhaps as a :term:`WSGI` app with help from the |
| | | :func:`pyramid.wsgi.wsgiapp2` decorator), these variables are |
| | | managed in a *stack* during normal system operations. The stack |
| | | instance itself is a :class:`threading.local`. |
| | | Because one :app:`Pyramid` application is permitted to call *another* |
| | | :app:`Pyramid` application from its own :term:`view` code (perhaps as a |
| | | :term:`WSGI` app with help from the :func:`pyramid.wsgi.wsgiapp2` decorator), |
| | | these variables are managed in a *stack* during normal system operations. The |
| | | stack instance itself is a :class:`threading.local`. |
| | | |
| | | During normal operations, the thread locals stack is managed by a |
| | | :term:`Router` object. At the beginning of a request, the Router |
| | | pushes the application's registry and the request on to the stack. At |
| | | the end of a request, the stack is popped. The topmost request and |
| | | registry on the stack are considered "current". Therefore, when the |
| | | system is operating normally, the very definition of "current" is |
| | | defined entirely by the behavior of a pyramid :term:`Router`. |
| | | :term:`Router` object. At the beginning of a request, the Router pushes the |
| | | application's registry and the request on to the stack. At the end of a |
| | | request, the stack is popped. The topmost request and registry on the stack |
| | | are considered "current". Therefore, when the system is operating normally, |
| | | the very definition of "current" is defined entirely by the behavior of a |
| | | pyramid :term:`Router`. |
| | | |
| | | However, during unit testing, no Router code is ever invoked, and the |
| | | definition of "current" is defined by the boundary between calls to |
| | | the :meth:`pyramid.config.Configurator.begin` and |
| | | :meth:`pyramid.config.Configurator.end` methods (or between |
| | | calls to the :func:`pyramid.testing.setUp` and |
| | | :func:`pyramid.testing.tearDown` functions). These functions push |
| | | and pop the threadlocal stack when the system is under test. See |
| | | :ref:`test_setup_and_teardown` for the definitions of these functions. |
| | | definition of "current" is defined by the boundary between calls to the |
| | | :meth:`pyramid.config.Configurator.begin` and |
| | | :meth:`pyramid.config.Configurator.end` methods (or between calls to the |
| | | :func:`pyramid.testing.setUp` and :func:`pyramid.testing.tearDown` functions). |
| | | These functions push and pop the threadlocal stack when the system is under |
| | | test. See :ref:`test_setup_and_teardown` for the definitions of these |
| | | functions. |
| | | |
| | | Scripts which use :app:`Pyramid` machinery but never actually start |
| | | a WSGI server or receive requests via HTTP such as scripts which use |
| | | the :mod:`pyramid.scripting` API will never cause any Router code |
| | | to be executed. However, the :mod:`pyramid.scripting` APIs also |
| | | push some values on to the thread locals stack as a matter of course. |
| | | Such scripts should expect the |
| | | :func:`~pyramid.threadlocal.get_current_request` function to always |
| | | return ``None``, and should expect the |
| | | :func:`~pyramid.threadlocal.get_current_registry` function to return |
| | | exactly the same :term:`application registry` for every request. |
| | | Scripts which use :app:`Pyramid` machinery but never actually start a WSGI |
| | | server or receive requests via HTTP, such as scripts which use the |
| | | :mod:`pyramid.scripting` API, will never cause any Router code to be executed. |
| | | However, the :mod:`pyramid.scripting` APIs also push some values on to the |
| | | thread locals stack as a matter of course. Such scripts should expect the |
| | | :func:`~pyramid.threadlocal.get_current_request` function to always return |
| | | ``None``, and should expect the |
| | | :func:`~pyramid.threadlocal.get_current_registry` function to return exactly |
| | | the same :term:`application registry` for every request. |
| | | |
| | | Why You Shouldn't Abuse Thread Locals |
| | | ------------------------------------- |
| | | |
| | | You probably should almost never use the |
| | | :func:`~pyramid.threadlocal.get_current_request` or |
| | | :func:`~pyramid.threadlocal.get_current_registry` functions, except |
| | | perhaps in tests. In particular, it's almost always a mistake to use |
| | | ``get_current_request`` or ``get_current_registry`` in application |
| | | code because its usage makes it possible to write code that can be |
| | | neither easily tested nor scripted. Inappropriate usage is defined as |
| | | follows: |
| | | :func:`~pyramid.threadlocal.get_current_registry` functions, except perhaps in |
| | | tests. In particular, it's almost always a mistake to use |
| | | ``get_current_request`` or ``get_current_registry`` in application code because |
| | | its usage makes it possible to write code that can be neither easily tested nor |
| | | scripted. Inappropriate usage is defined as follows: |
| | | |
| | | - ``get_current_request`` should never be called within the body of a |
| | | :term:`view callable`, or within code called by a view callable. |
| | | View callables already have access to the request (it's passed in to |
| | | each as ``request``). |
| | | :term:`view callable`, or within code called by a view callable. View |
| | | callables already have access to the request (it's passed in to each as |
| | | ``request``). |
| | | |
| | | - ``get_current_request`` should never be called in :term:`resource` code. |
| | | If a resource needs access to the request, it should be passed the request |
| | | by a :term:`view callable`. |
| | | - ``get_current_request`` should never be called in :term:`resource` code. If a |
| | | resource needs access to the request, it should be passed the request by a |
| | | :term:`view callable`. |
| | | |
| | | - ``get_current_request`` function should never be called because it's |
| | | "easier" or "more elegant" to think about calling it than to pass a |
| | | request through a series of function calls when creating some API |
| | | design. Your application should instead almost certainly pass data |
| | | derived from the request around rather than relying on being able to |
| | | call this function to obtain the request in places that actually |
| | | have no business knowing about it. Parameters are *meant* to be |
| | | passed around as function arguments, this is why they exist. Don't |
| | | try to "save typing" or create "nicer APIs" by using this function |
| | | in the place where a request is required; this will only lead to |
| | | sadness later. |
| | | - ``get_current_request`` function should never be called because it's "easier" |
| | | or "more elegant" to think about calling it than to pass a request through a |
| | | series of function calls when creating some API design. Your application |
| | | should instead, almost certainly, pass around data derived from the request |
| | | rather than relying on being able to call this function to obtain the request |
| | | in places that actually have no business knowing about it. Parameters are |
| | | *meant* to be passed around as function arguments; this is why they exist. |
| | | Don't try to "save typing" or create "nicer APIs" by using this function in |
| | | the place where a request is required; this will only lead to sadness later. |
| | | |
| | | - Neither ``get_current_request`` nor ``get_current_registry`` should |
| | | ever be called within application-specific forks of third-party |
| | | library code. The library you've forked almost certainly has |
| | | nothing to do with :app:`Pyramid`, and making it dependent on |
| | | :app:`Pyramid` (rather than making your :app:`pyramid` |
| | | application depend upon it) means you're forming a dependency in the |
| | | wrong direction. |
| | | - Neither ``get_current_request`` nor ``get_current_registry`` should ever be |
| | | called within application-specific forks of third-party library code. The |
| | | library you've forked almost certainly has nothing to do with :app:`Pyramid`, |
| | | and making it dependent on :app:`Pyramid` (rather than making your |
| | | :app:`pyramid` application depend upon it) means you're forming a dependency |
| | | in the wrong direction. |
| | | |
| | | Use of the :func:`~pyramid.threadlocal.get_current_request` function |
| | | in application code *is* still useful in very limited circumstances. |
| | | As a rule of thumb, usage of ``get_current_request`` is useful |
| | | **within code which is meant to eventually be removed**. For |
| | | instance, you may find yourself wanting to deprecate some API that |
| | | expects to be passed a request object in favor of one that does not |
| | | expect to be passed a request object. But you need to keep |
| | | implementations of the old API working for some period of time while |
| | | you deprecate the older API. So you write a "facade" implementation |
| | | of the new API which calls into the code which implements the older |
| | | API. Since the new API does not require the request, your facade |
| | | implementation doesn't have local access to the request when it needs |
| | | to pass it into the older API implementation. After some period of |
| | | time, the older implementation code is disused and the hack that uses |
| | | ``get_current_request`` is removed. This would be an appropriate |
| | | place to use the ``get_current_request``. |
| | | Use of the :func:`~pyramid.threadlocal.get_current_request` function in |
| | | application code *is* still useful in very limited circumstances. As a rule of |
| | | thumb, usage of ``get_current_request`` is useful **within code which is meant |
| | | to eventually be removed**. For instance, you may find yourself wanting to |
| | | deprecate some API that expects to be passed a request object in favor of one |
| | | that does not expect to be passed a request object. But you need to keep |
| | | implementations of the old API working for some period of time while you |
| | | deprecate the older API. So you write a "facade" implementation of the new API |
| | | which calls into the code which implements the older API. Since the new API |
| | | does not require the request, your facade implementation doesn't have local |
| | | access to the request when it needs to pass it into the older API |
| | | implementation. After some period of time, the older implementation code is |
| | | disused and the hack that uses ``get_current_request`` is removed. This would |
| | | be an appropriate place to use the ``get_current_request``. |
| | | |
| | | Use of the :func:`~pyramid.threadlocal.get_current_registry` |
| | | function should be limited to testing scenarios. The registry made |
| | | current by use of the |
| | | :meth:`pyramid.config.Configurator.begin` method during a |
| | | test (or via :func:`pyramid.testing.setUp`) when you do not pass |
| | | one in is available to you via this API. |
| | | |
| | | Use of the :func:`~pyramid.threadlocal.get_current_registry` function should be |
| | | limited to testing scenarios. The registry made current by use of the |
| | | :meth:`pyramid.config.Configurator.begin` method during a test (or via |
| | | :func:`pyramid.testing.setUp`) when you do not pass one in is available to you |
| | | via this API. |
| | |
| | | Traversal |
| | | ========= |
| | | |
| | | This chapter explains the technical details of how traversal works in |
| | | Pyramid. |
| | | This chapter explains the technical details of how traversal works in Pyramid. |
| | | |
| | | For a quick example, see :doc:`hellotraversal`. |
| | | |
| | | For more about *why* you might use traversal, see :doc:`muchadoabouttraversal`. |
| | | |
| | | A :term:`traversal` uses the URL (Universal Resource Locator) to find a |
| | | :term:`resource` located in a :term:`resource tree`, which is a set of |
| | | nested dictionary-like objects. Traversal is done by using each segment |
| | | of the path portion of the URL to navigate through the :term:`resource |
| | | tree`. You might think of this as looking up files and directories in a |
| | | file system. Traversal walks down the path until it finds a published |
| | | resource, analogous to a file system "directory" or "file". The |
| | | resource found as the result of a traversal becomes the |
| | | :term:`context` of the :term:`request`. Then, the :term:`view lookup` |
| | | subsystem is used to find some view code willing to "publish" this |
| | | :term:`resource` located in a :term:`resource tree`, which is a set of nested |
| | | dictionary-like objects. Traversal is done by using each segment of the path |
| | | portion of the URL to navigate through the :term:`resource tree`. You might |
| | | think of this as looking up files and directories in a file system. Traversal |
| | | walks down the path until it finds a published resource, analogous to a file |
| | | system "directory" or "file". The resource found as the result of a traversal |
| | | becomes the :term:`context` of the :term:`request`. Then, the :term:`view |
| | | lookup` subsystem is used to find some view code willing to "publish" this |
| | | resource by generating a :term:`response`. |
| | | |
| | | .. note:: |
| | | |
| | | Using :term:`Traversal` to map a URL to code is optional. If you're creating |
| | | your first Pyramid application it probably makes more sense to use :term:`URL |
| | | dispatch` to map URLs to code instead of traversal, as new Pyramid developers |
| | | tend to find URL dispatch slightly easier to understand. If you use URL |
| | | dispatch, you needn't read this chapter. |
| | | your first Pyramid application, it probably makes more sense to use |
| | | :term:`URL dispatch` to map URLs to code instead of traversal, as new Pyramid |
| | | developers tend to find URL dispatch slightly easier to understand. If you |
| | | use URL dispatch, you needn't read this chapter. |
| | | |
| | | .. index:: |
| | | single: traversal details |
| | |
| | | Traversal Details |
| | | ----------------- |
| | | |
| | | :term:`Traversal` is dependent on information in a :term:`request` |
| | | object. Every :term:`request` object contains URL path information in |
| | | the ``PATH_INFO`` portion of the :term:`WSGI` environment. The |
| | | ``PATH_INFO`` string is the portion of a request's URL following the |
| | | hostname and port number, but before any query string elements or |
| | | fragment element. For example the ``PATH_INFO`` portion of the URL |
| | | ``http://example.com:8080/a/b/c?foo=1`` is ``/a/b/c``. |
| | | :term:`Traversal` is dependent on information in a :term:`request` object. |
| | | Every :term:`request` object contains URL path information in the ``PATH_INFO`` |
| | | portion of the :term:`WSGI` environment. The ``PATH_INFO`` string is the |
| | | portion of a request's URL following the hostname and port number, but before |
| | | any query string elements or fragment element. For example the ``PATH_INFO`` |
| | | portion of the URL ``http://example.com:8080/a/b/c?foo=1`` is ``/a/b/c``. |
| | | |
| | | Traversal treats the ``PATH_INFO`` segment of a URL as a sequence of |
| | | path segments. For example, the ``PATH_INFO`` string ``/a/b/c`` is |
| | | converted to the sequence ``['a', 'b', 'c']``. |
| | | Traversal treats the ``PATH_INFO`` segment of a URL as a sequence of path |
| | | segments. For example, the ``PATH_INFO`` string ``/a/b/c`` is converted to the |
| | | sequence ``['a', 'b', 'c']``. |
| | | |
| | | This path sequence is then used to descend through the :term:`resource |
| | | tree`, looking up a resource for each path segment. Each lookup uses the |
| | | This path sequence is then used to descend through the :term:`resource tree`, |
| | | looking up a resource for each path segment. Each lookup uses the |
| | | ``__getitem__`` method of a resource in the tree. |
| | | |
| | | For example, if the path info sequence is ``['a', 'b', 'c']``: |
| | | |
| | | - :term:`Traversal` starts by acquiring the :term:`root` resource of the |
| | | application by calling the :term:`root factory`. The :term:`root factory` |
| | | can be configured to return whatever object is appropriate as the |
| | | traversal root of your application. |
| | | application by calling the :term:`root factory`. The :term:`root factory` can |
| | | be configured to return whatever object is appropriate as the traversal root |
| | | of your application. |
| | | |
| | | - Next, the first element (``'a'``) is popped from the path segment |
| | | sequence and is used as a key to lookup the corresponding resource |
| | | in the root. This invokes the root resource's ``__getitem__`` method |
| | | using that value (``'a'``) as an argument. |
| | | - Next, the first element (``'a'``) is popped from the path segment sequence |
| | | and is used as a key to lookup the corresponding resource in the root. This |
| | | invokes the root resource's ``__getitem__`` method using that value (``'a'``) |
| | | as an argument. |
| | | |
| | | - If the root resource "contains" a resource with key ``'a'``, its |
| | | ``__getitem__`` method will return it. The :term:`context` temporarily |
| | |
| | | resource's ``__getitem__`` is called with that value (``'b'``) as an |
| | | argument; we'll presume it succeeds. |
| | | |
| | | - The "A" resource's ``__getitem__`` returns another resource, which |
| | | we'll call "B". The :term:`context` temporarily becomes the "B" |
| | | resource. |
| | | - The "A" resource's ``__getitem__`` returns another resource, which we'll call |
| | | "B". The :term:`context` temporarily becomes the "B" resource. |
| | | |
| | | Traversal continues until the path segment sequence is exhausted or a |
| | | path element cannot be resolved to a resource. In either case, the |
| | | :term:`context` resource is the last object that the traversal |
| | | successfully resolved. If any resource found during traversal lacks a |
| | | ``__getitem__`` method, or if its ``__getitem__`` method raises a |
| | | :exc:`KeyError`, traversal ends immediately, and that resource becomes |
| | | the :term:`context`. |
| | | Traversal continues until the path segment sequence is exhausted or a path |
| | | element cannot be resolved to a resource. In either case, the :term:`context` |
| | | resource is the last object that the traversal successfully resolved. If any |
| | | resource found during traversal lacks a ``__getitem__`` method, or if its |
| | | ``__getitem__`` method raises a :exc:`KeyError`, traversal ends immediately, |
| | | and that resource becomes the :term:`context`. |
| | | |
| | | The results of a :term:`traversal` also include a :term:`view name`. If |
| | | traversal ends before the path segment sequence is exhausted, the |
| | | :term:`view name` is the *next* remaining path segment element. If the |
| | | :term:`traversal` expends all of the path segments, then the :term:`view |
| | | name` is the empty string (``''``). |
| | | traversal ends before the path segment sequence is exhausted, the :term:`view |
| | | name` is the *next* remaining path segment element. If the :term:`traversal` |
| | | expends all of the path segments, then the :term:`view name` is the empty |
| | | string (``''``). |
| | | |
| | | The combination of the context resource and the :term:`view name` found |
| | | via traversal is used later in the same request by the :term:`view |
| | | lookup` subsystem to find a :term:`view callable`. How :app:`Pyramid` |
| | | performs view lookup is explained within the :ref:`view_config_chapter` |
| | | chapter. |
| | | The combination of the context resource and the :term:`view name` found via |
| | | traversal is used later in the same request by the :term:`view lookup` |
| | | subsystem to find a :term:`view callable`. How :app:`Pyramid` performs view |
| | | lookup is explained within the :ref:`view_config_chapter` chapter. |
| | | |
| | | .. index:: |
| | | single: object tree |
| | |
| | | The Resource Tree |
| | | ----------------- |
| | | |
| | | The resource tree is a set of nested dictionary-like resource objects |
| | | that begins with a :term:`root` resource. In order to use |
| | | :term:`traversal` to resolve URLs to code, your application must supply |
| | | a :term:`resource tree` to :app:`Pyramid`. |
| | | The resource tree is a set of nested dictionary-like resource objects that |
| | | begins with a :term:`root` resource. In order to use :term:`traversal` to |
| | | resolve URLs to code, your application must supply a :term:`resource tree` to |
| | | :app:`Pyramid`. |
| | | |
| | | In order to supply a root resource for an application the :app:`Pyramid` |
| | | :term:`Router` is configured with a callback known as a :term:`root |
| | | factory`. The root factory is supplied by the application, at startup |
| | | time, as the ``root_factory`` argument to the :term:`Configurator`. |
| | | :term:`Router` is configured with a callback known as a :term:`root factory`. |
| | | The root factory is supplied by the application at startup time as the |
| | | ``root_factory`` argument to the :term:`Configurator`. |
| | | |
| | | The root factory is a Python callable that accepts a :term:`request` |
| | | object, and returns the root object of the :term:`resource tree`. A |
| | | function, or class is typically used as an application's root factory. |
| | | Here's an example of a simple root factory class: |
| | | The root factory is a Python callable that accepts a :term:`request` object, |
| | | and returns the root object of the :term:`resource tree`. A function or class |
| | | is typically used as an application's root factory. Here's an example of a |
| | | simple root factory class: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | config = Configurator(root_factory=Root) |
| | | |
| | | The ``root_factory`` argument to the |
| | | :class:`~pyramid.config.Configurator` constructor registers this root |
| | | factory to be called to generate a root resource whenever a request |
| | | enters the application. The root factory registered this way is also |
| | | known as the global root factory. A root factory can alternately be |
| | | passed to the ``Configurator`` as a :term:`dotted Python name` which can |
| | | refer to a root factory defined in a different module. |
| | | The ``root_factory`` argument to the :class:`~pyramid.config.Configurator` |
| | | constructor registers this root factory to be called to generate a root |
| | | resource whenever a request enters the application. The root factory |
| | | registered this way is also known as the global root factory. A root factory |
| | | can alternatively be passed to the ``Configurator`` as a :term:`dotted Python |
| | | name` which can refer to a root factory defined in a different module. |
| | | |
| | | If no :term:`root factory` is passed to the :app:`Pyramid` |
| | | :term:`Configurator` constructor, or if the ``root_factory`` value |
| | | specified is ``None``, a :term:`default root factory` is used. The default |
| | | root factory always returns a resource that has no child resources; it |
| | | is effectively empty. |
| | | If no :term:`root factory` is passed to the :app:`Pyramid` :term:`Configurator` |
| | | constructor, or if the ``root_factory`` value specified is ``None``, a |
| | | :term:`default root factory` is used. The default root factory always returns |
| | | a resource that has no child resources; it is effectively empty. |
| | | |
| | | Usually a root factory for a traversal-based application will be more |
| | | complicated than the above ``Root`` class; in particular it may be associated |
| | | complicated than the above ``Root`` class. In particular it may be associated |
| | | with a database connection or another persistence mechanism. The above |
| | | ``Root`` class is analogous to the default root factory present in Pyramid. The |
| | | default root factory is very simple and not very useful. |
| | | |
| | | .. note:: |
| | | |
| | | If the items contained within the resource tree are "persistent" (they |
| | | have state that lasts longer than the execution of a single process), they |
| | | become analogous to the concept of :term:`domain model` objects used by |
| | | many other frameworks. |
| | | If the items contained within the resource tree are "persistent" (they have |
| | | state that lasts longer than the execution of a single process), they become |
| | | analogous to the concept of :term:`domain model` objects used by many other |
| | | frameworks. |
| | | |
| | | The resource tree consists of *container* resources and *leaf* resources. |
| | | There is only one difference between a *container* resource and a *leaf* |
| | | resource: *container* resources possess a ``__getitem__`` method (making it |
| | | The resource tree consists of *container* resources and *leaf* resources. There |
| | | is only one difference between a *container* resource and a *leaf* resource: |
| | | *container* resources possess a ``__getitem__`` method (making it |
| | | "dictionary-like") while *leaf* resources do not. The ``__getitem__`` method |
| | | was chosen as the signifying difference between the two types of resources |
| | | because the presence of this method is how Python itself typically determines |
| | | whether an object is "containerish" or not (dictionary objects are |
| | | "containerish"). |
| | | |
| | | Each container resource is presumed to be willing to return a child resource |
| | | or raise a ``KeyError`` based on a name passed to its ``__getitem__``. |
| | | Each container resource is presumed to be willing to return a child resource or |
| | | raise a ``KeyError`` based on a name passed to its ``__getitem__``. |
| | | |
| | | Leaf-level instances must not have a ``__getitem__``. If instances that |
| | | you'd like to be leaves already happen to have a ``__getitem__`` through some |
| | | Leaf-level instances must not have a ``__getitem__``. If instances that you'd |
| | | like to be leaves already happen to have a ``__getitem__`` through some |
| | | historical inequity, you should subclass these resource types and cause their |
| | | ``__getitem__`` methods to simply raise a ``KeyError``. Or just disuse them |
| | | and think up another strategy. |
| | | |
| | | Usually, the traversal root is a *container* resource, and as such it |
| | | contains other resources. However, it doesn't *need* to be a container. |
| | | Your resource tree can be as shallow or as deep as you require. |
| | | Usually the traversal root is a *container* resource, and as such it contains |
| | | other resources. However, it doesn't *need* to be a container. Your resource |
| | | tree can be as shallow or as deep as you require. |
| | | |
| | | In general, the resource tree is traversed beginning at its root resource |
| | | using a sequence of path elements described by the ``PATH_INFO`` of the |
| | | current request; if there are path segments, the root resource's |
| | | ``__getitem__`` is called with the next path segment, and it is expected to |
| | | return another resource. The resulting resource's ``__getitem__`` is called |
| | | with the very next path segment, and it is expected to return another |
| | | resource. This happens *ad infinitum* until all path segments are exhausted. |
| | | In general, the resource tree is traversed beginning at its root resource using |
| | | a sequence of path elements described by the ``PATH_INFO`` of the current |
| | | request. If there are path segments, the root resource's ``__getitem__`` is |
| | | called with the next path segment, and it is expected to return another |
| | | resource. The resulting resource's ``__getitem__`` is called with the very |
| | | next path segment, and it is expected to return another resource. This happens |
| | | *ad infinitum* until all path segments are exhausted. |
| | | |
| | | .. index:: |
| | | single: traversal algorithm |
| | |
| | | |
| | | This section will attempt to explain the :app:`Pyramid` traversal algorithm. |
| | | We'll provide a description of the algorithm, a diagram of how the algorithm |
| | | works, and some example traversal scenarios that might help you understand |
| | | how the algorithm operates against a specific resource tree. |
| | | works, and some example traversal scenarios that might help you understand how |
| | | the algorithm operates against a specific resource tree. |
| | | |
| | | We'll also talk a bit about :term:`view lookup`. The |
| | | :ref:`view_config_chapter` chapter discusses :term:`view lookup` in |
| | | detail, and it is the canonical source for information about views. |
| | | Technically, :term:`view lookup` is a :app:`Pyramid` subsystem that is |
| | | separated from traversal entirely. However, we'll describe the |
| | | fundamental behavior of view lookup in the examples in the next few |
| | | sections to give you an idea of how traversal and view lookup cooperate, |
| | | because they are almost always used together. |
| | | :ref:`view_config_chapter` chapter discusses :term:`view lookup` in detail, and |
| | | it is the canonical source for information about views. Technically, |
| | | :term:`view lookup` is a :app:`Pyramid` subsystem that is separated from |
| | | traversal entirely. However, we'll describe the fundamental behavior of view |
| | | lookup in the examples in the next few sections to give you an idea of how |
| | | traversal and view lookup cooperate, because they are almost always used |
| | | together. |
| | | |
| | | .. index:: |
| | | single: view name |
| | |
| | | single: root factory |
| | | single: default view |
| | | |
| | | A Description of The Traversal Algorithm |
| | | A Description of the Traversal Algorithm |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | When a user requests a page from your traversal-powered application, the |
| | | system uses this algorithm to find a :term:`context` resource and a |
| | | :term:`view name`. |
| | | When a user requests a page from your traversal-powered application, the system |
| | | uses this algorithm to find a :term:`context` resource and a :term:`view name`. |
| | | |
| | | #. The request for the page is presented to the :app:`Pyramid` |
| | | :term:`router` in terms of a standard :term:`WSGI` request, which is |
| | | represented by a WSGI environment and a WSGI ``start_response`` callable. |
| | | #. The request for the page is presented to the :app:`Pyramid` :term:`router` |
| | | in terms of a standard :term:`WSGI` request, which is represented by a WSGI |
| | | environment and a WSGI ``start_response`` callable. |
| | | |
| | | #. The router creates a :term:`request` object based on the WSGI |
| | | environment. |
| | | #. The router creates a :term:`request` object based on the WSGI environment. |
| | | |
| | | #. The :term:`root factory` is called with the :term:`request`. It returns |
| | | a :term:`root` resource. |
| | | #. The :term:`root factory` is called with the :term:`request`. It returns a |
| | | :term:`root` resource. |
| | | |
| | | #. The router uses the WSGI environment's ``PATH_INFO`` information to |
| | | determine the path segments to traverse. The leading slash is stripped |
| | | off ``PATH_INFO``, and the remaining path segments are split on the slash |
| | | determine the path segments to traverse. The leading slash is stripped off |
| | | ``PATH_INFO``, and the remaining path segments are split on the slash |
| | | character to form a traversal sequence. |
| | | |
| | | The traversal algorithm by default attempts to first URL-unquote and then |
| | |
| | | Conversion from a URL-decoded string into Unicode is attempted using the |
| | | UTF-8 encoding. If any URL-unquoted path segment in ``PATH_INFO`` is not |
| | | decodeable using the UTF-8 decoding, a :exc:`TypeError` is raised. A |
| | | segment will be fully URL-unquoted and UTF8-decoded before it is passed |
| | | in to the ``__getitem__`` of any resource during traversal. |
| | | segment will be fully URL-unquoted and UTF8-decoded before it is passed in |
| | | to the ``__getitem__`` of any resource during traversal. |
| | | |
| | | Thus, a request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the |
| | | Thus a request with a ``PATH_INFO`` variable of ``/a/b/c`` maps to the |
| | | traversal sequence ``[u'a', u'b', u'c']``. |
| | | |
| | | #. :term:`Traversal` begins at the root resource returned by the root |
| | | factory. For the traversal sequence ``[u'a', u'b', u'c']``, the root |
| | | resource's ``__getitem__`` is called with the name ``'a'``. Traversal |
| | | continues through the sequence. In our example, if the root resource's |
| | | ``__getitem__`` called with the name ``a`` returns a resource (aka |
| | | #. :term:`Traversal` begins at the root resource returned by the root factory. |
| | | For the traversal sequence ``[u'a', u'b', u'c']``, the root resource's |
| | | ``__getitem__`` is called with the name ``'a'``. Traversal continues |
| | | through the sequence. In our example, if the root resource's |
| | | ``__getitem__`` called with the name ``a`` returns a resource (a.k.a. |
| | | resource "A"), that resource's ``__getitem__`` is called with the name |
| | | ``'b'``. If resource "A" returns a resource "B" when asked for ``'b'``, |
| | | resource B's ``__getitem__`` is then asked for the name ``'c'``, and may |
| | | return resource "C". |
| | | |
| | | #. Traversal ends when a) the entire path is exhausted or b) when any |
| | | resource raises a :exc:`KeyError` from its ``__getitem__`` or c) when any |
| | | #. Traversal ends when either (a) the entire path is exhausted, (b) when any |
| | | resource raises a :exc:`KeyError` from its ``__getitem__``, (c) when any |
| | | non-final path element traversal does not have a ``__getitem__`` method |
| | | (resulting in a :exc:`AttributeError`) or d) when any path element is |
| | | (resulting in an :exc:`AttributeError`), or (d) when any path element is |
| | | prefixed with the set of characters ``@@`` (indicating that the characters |
| | | following the ``@@`` token should be treated as a :term:`view name`). |
| | | |
| | |
| | | resource found during traversal is deemed to be the :term:`context`. If |
| | | the path has been exhausted when traversal ends, the :term:`view name` is |
| | | deemed to be the empty string (``''``). However, if the path was *not* |
| | | exhausted before traversal terminated, the first remaining path segment |
| | | is treated as the view name. |
| | | exhausted before traversal terminated, the first remaining path segment is |
| | | treated as the view name. |
| | | |
| | | #. Any subsequent path elements after the :term:`view name` is found are |
| | | deemed the :term:`subpath`. The subpath is always a sequence of path |
| | | segments that come from ``PATH_INFO`` that are "left over" after |
| | | traversal has completed. |
| | | segments that come from ``PATH_INFO`` that are "left over" after traversal |
| | | has completed. |
| | | |
| | | Once the :term:`context` resource, the :term:`view name`, and associated |
| | | attributes such as the :term:`subpath` are located, the job of |
| | |
| | | |
| | | - You will often end up with a :term:`view name` that is the empty string as |
| | | the result of a particular traversal. This indicates that the view lookup |
| | | machinery should look up the :term:`default view`. The default view is a |
| | | view that is registered with no name or a view which is registered with a |
| | | name that equals the empty string. |
| | | machinery should lookup the :term:`default view`. The default view is a view |
| | | that is registered with no name or a view which is registered with a name |
| | | that equals the empty string. |
| | | |
| | | - If any path segment element begins with the special characters ``@@`` |
| | | (think of them as goggles), the value of that segment minus the goggle |
| | | characters is considered the :term:`view name` immediately and traversal |
| | | stops there. This allows you to address views that may have the same names |
| | | as resource names in the tree unambiguously. |
| | | - If any path segment element begins with the special characters ``@@`` (think |
| | | of them as goggles), the value of that segment minus the goggle characters is |
| | | considered the :term:`view name` immediately and traversal stops there. This |
| | | allows you to address views that may have the same names as resource names in |
| | | the tree unambiguously. |
| | | |
| | | Finally, traversal is responsible for locating a :term:`virtual root`. A |
| | | virtual root is used during "virtual hosting"; see the |
| | | :ref:`vhosting_chapter` chapter for information. We won't speak more about |
| | | it in this chapter. |
| | | virtual root is used during "virtual hosting". See the :ref:`vhosting_chapter` |
| | | chapter for information. We won't speak more about it in this chapter. |
| | | |
| | | .. image:: resourcetreetraverser.png |
| | | |
| | |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | No one can be expected to understand the traversal algorithm by analogy and |
| | | description alone, so let's examine some traversal scenarios that use |
| | | concrete URLs and resource tree compositions. |
| | | description alone, so let's examine some traversal scenarios that use concrete |
| | | URLs and resource tree compositions. |
| | | |
| | | Let's pretend the user asks for |
| | | ``http://example.com/foo/bar/baz/biz/buz.txt``. The request's ``PATH_INFO`` |
| | | in that case is ``/foo/bar/baz/biz/buz.txt``. Let's further pretend that |
| | | when this request comes in that we're traversing the following resource tree: |
| | | Let's pretend the user asks for ``http://example.com/foo/bar/baz/biz/buz.txt``. |
| | | The request's ``PATH_INFO`` in that case is ``/foo/bar/baz/biz/buz.txt``. |
| | | Let's further pretend that when this request comes in, we're traversing the |
| | | following resource tree: |
| | | |
| | | .. code-block:: text |
| | | |
| | |
| | | finds. |
| | | |
| | | - :term:`traversal` traverses "bar", and attempts to find "baz", which it does |
| | | not find (the "bar" resource raises a :exc:`KeyError` when asked for |
| | | "baz"). |
| | | not find (the "bar" resource raises a :exc:`KeyError` when asked for "baz"). |
| | | |
| | | The fact that it does not find "baz" at this point does not signify an error |
| | | condition. It signifies that: |
| | | condition. It signifies the following: |
| | | |
| | | - the :term:`context` is the "bar" resource (the context is the last resource |
| | | - The :term:`context` is the "bar" resource (the context is the last resource |
| | | found during traversal). |
| | | |
| | | - the :term:`view name` is ``baz`` |
| | | - The :term:`view name` is ``baz``. |
| | | |
| | | - the :term:`subpath` is ``('biz', 'buz.txt')`` |
| | | - The :term:`subpath` is ``('biz', 'buz.txt')``. |
| | | |
| | | At this point, traversal has ended, and :term:`view lookup` begins. |
| | | |
| | | Because it's the "context" resource, the view lookup machinery examines "bar" |
| | | to find out what "type" it is. Let's say it finds that the context is a |
| | | ``Bar`` type (because "bar" happens to be an instance of the class ``Bar``). |
| | | Using the :term:`view name` (``baz``) and the type, view lookup asks the |
| | | to find out what "type" it is. Let's say it finds that the context is a ``Bar`` |
| | | type (because "bar" happens to be an instance of the class ``Bar``). Using the |
| | | :term:`view name` (``baz``) and the type, view lookup asks the |
| | | :term:`application registry` this question: |
| | | |
| | | - Please find me a :term:`view callable` registered using a :term:`view |
| | | configuration` with the name "baz" that can be used for the class ``Bar``. |
| | | |
| | | Let's say that view lookup finds no matching view type. In this |
| | | circumstance, the :app:`Pyramid` :term:`router` returns the result of the |
| | | :term:`Not Found View` and the request ends. |
| | | Let's say that view lookup finds no matching view type. In this circumstance, |
| | | the :app:`Pyramid` :term:`router` returns the result of the :term:`Not Found |
| | | View` and the request ends. |
| | | |
| | | However, for this tree: |
| | | |
| | |
| | | - :term:`traversal` traverses "baz", and attempts to find "biz", which it |
| | | finds. |
| | | |
| | | - :term:`traversal` traverses "biz", and attempts to find "buz.txt" which it |
| | | - :term:`traversal` traverses "biz", and attempts to find "buz.txt", which it |
| | | does not find. |
| | | |
| | | The fact that it does not find a resource related to "buz.txt" at this point |
| | | does not signify an error condition. It signifies that: |
| | | does not signify an error condition. It signifies the following: |
| | | |
| | | - the :term:`context` is the "biz" resource (the context is the last resource |
| | | - The :term:`context` is the "biz" resource (the context is the last resource |
| | | found during traversal). |
| | | |
| | | - the :term:`view name` is "buz.txt" |
| | | - The :term:`view name` is "buz.txt". |
| | | |
| | | - the :term:`subpath` is an empty sequence ( ``()`` ). |
| | | - The :term:`subpath` is an empty sequence ( ``()`` ). |
| | | |
| | | At this point, traversal has ended, and :term:`view lookup` begins. |
| | | |
| | | Because it's the "context" resource, the view lookup machinery examines the |
| | | "biz" resource to find out what "type" it is. Let's say it finds that the |
| | | resource is a ``Biz`` type (because "biz" is an instance of the Python class |
| | | ``Biz``). Using the :term:`view name` (``buz.txt``) and the type, view |
| | | lookup asks the :term:`application registry` this question: |
| | | ``Biz``). Using the :term:`view name` (``buz.txt``) and the type, view lookup |
| | | asks the :term:`application registry` this question: |
| | | |
| | | - Please find me a :term:`view callable` registered with a :term:`view |
| | | configuration` with the name ``buz.txt`` that can be used for class |
| | | ``Biz``. |
| | | configuration` with the name ``buz.txt`` that can be used for class ``Biz``. |
| | | |
| | | Let's say that question is answered by the application registry; in such a |
| | | situation, the application registry returns a :term:`view callable`. The |
| | | view callable is then called with the current :term:`WebOb` :term:`request` |
| | | as the sole argument: ``request``; it is expected to return a response. |
| | | Let's say that question is answered by the application registry. In such a |
| | | situation, the application registry returns a :term:`view callable`. The view |
| | | callable is then called with the current :term:`WebOb` :term:`request` as the |
| | | sole argument, ``request``. It is expected to return a response. |
| | | |
| | | .. sidebar:: The Example View Callables Accept Only a Request; How Do I Access the Context Resource? |
| | | .. sidebar:: The Example View Callables Accept Only a Request; How Do I Access |
| | | the Context Resource? |
| | | |
| | | Most of the examples in this book assume that a view callable is typically |
| | | passed only a :term:`request` object. Sometimes your view callables need |
| | | access to the :term:`context` resource, especially when you use |
| | | :term:`traversal`. You might use a supported alternate view callable |
| | | Most of the examples in this documentation assume that a view callable is |
| | | typically passed only a :term:`request` object. Sometimes your view |
| | | callables need access to the :term:`context` resource, especially when you |
| | | use :term:`traversal`. You might use a supported alternative view callable |
| | | argument list in your view callables such as the ``(context, request)`` |
| | | calling convention described in |
| | | :ref:`request_and_context_view_definitions`. But you don't need to if you |
| | | don't want to. In view callables that accept only a request, the |
| | | :term:`context` resource found by traversal is available as the |
| | | ``context`` attribute of the request object, e.g. ``request.context``. |
| | | The :term:`view name` is available as the ``view_name`` attribute of the |
| | | request object, e.g. ``request.view_name``. Other :app:`Pyramid` |
| | | -specific request attributes are also available as described in |
| | | :ref:`special_request_attributes`. |
| | | calling convention described in :ref:`request_and_context_view_definitions`. |
| | | But you don't need to if you don't want to. In view callables that accept |
| | | only a request, the :term:`context` resource found by traversal is available |
| | | as the ``context`` attribute of the request object, e.g., |
| | | ``request.context``. The :term:`view name` is available as the ``view_name`` |
| | | attribute of the request object, e.g., ``request.view_name``. Other |
| | | :app:`Pyramid`-specific request attributes are also available as described |
| | | in :ref:`special_request_attributes`. |
| | | |
| | | .. index:: |
| | | single: resource interfaces |
| | | |
| | | .. _using_resource_interfaces: |
| | | |
| | | Using Resource Interfaces In View Configuration |
| | | Using Resource Interfaces in View Configuration |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | Instead of registering your views with a ``context`` that names a Python |
| | |
| | | arbitrarily to any resource object. View lookup treats context interfaces |
| | | specially, and therefore the identity of a resource can be divorced from that |
| | | of the class which implements it. As a result, associating a view with an |
| | | interface can provide more flexibility for sharing a single view between two |
| | | or more different implementations of a resource type. For example, if two |
| | | resource objects of different Python class types share the same interface, |
| | | you can use the same view configuration to specify both of them as a |
| | | ``context``. |
| | | interface can provide more flexibility for sharing a single view between two or |
| | | more different implementations of a resource type. For example, if two |
| | | resource objects of different Python class types share the same interface, you |
| | | can use the same view configuration to specify both of them as a ``context``. |
| | | |
| | | In order to make use of interfaces in your application during view dispatch, |
| | | you must create an interface and mark up your resource classes or instances |
| | | with interface declarations that refer to this interface. |
| | | |
| | | To attach an interface to a resource *class*, you define the interface and |
| | | use the :func:`zope.interface.implementer` class decorator to associate the |
| | | To attach an interface to a resource *class*, you define the interface and use |
| | | the :func:`zope.interface.implementer` class decorator to associate the |
| | | interface with the class. |
| | | |
| | | .. code-block:: python |
| | |
| | | pass |
| | | |
| | | To attach an interface to a resource *instance*, you define the interface and |
| | | use the :func:`zope.interface.alsoProvides` function to associate the |
| | | interface with the instance. This function mutates the instance in such a |
| | | way that the interface is attached to it. |
| | | use the :func:`zope.interface.alsoProvides` function to associate the interface |
| | | with the instance. This function mutates the instance in such a way that the |
| | | interface is attached to it. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | alsoProvides(hello, IHello) |
| | | return hello |
| | | |
| | | Regardless of how you associate an interface, with a resource instance, or a |
| | | resource class, the resulting code to associate that interface with a view |
| | | Regardless of how you associate an interface—with either a resource instance |
| | | or a resource class—the resulting code to associate that interface with a view |
| | | callable is the same. Assuming the above code that defines an ``IHello`` |
| | | interface lives in the root of your application, and its module is named |
| | | "resources.py", the interface declaration below will associate the |
| | | ``mypackage.views.hello_world`` view with resources that implement, or |
| | | provide, this interface. |
| | | ``mypackage.views.hello_world`` view with resources that implement, or provide, |
| | | this interface. |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | config.add_view('mypackage.views.hello_world', name='hello.html', |
| | | context='mypackage.resources.IHello') |
| | | |
| | | Any time a resource that is determined to be the :term:`context` provides |
| | | this interface, and a view named ``hello.html`` is looked up against it as |
| | | per the URL, the ``mypackage.views.hello_world`` view callable will be |
| | | invoked. |
| | | Any time a resource that is determined to be the :term:`context` provides this |
| | | interface, and a view named ``hello.html`` is looked up against it as per the |
| | | URL, the ``mypackage.views.hello_world`` view callable will be invoked. |
| | | |
| | | Note, in cases where a view is registered against a resource class, and a |
| | | view is also registered against an interface that the resource class |
| | | implements, an ambiguity arises. Views registered for the resource class take |
| | | precedence over any views registered for any interface the resource class |
| | | implements. Thus, if one view configuration names a ``context`` of both the |
| | | class type of a resource, and another view configuration names a ``context`` |
| | | of interface implemented by the resource's class, and both view |
| | | configurations are otherwise identical, the view registered for the context's |
| | | class will "win". |
| | | Note, in cases where a view is registered against a resource class, and a view |
| | | is also registered against an interface that the resource class implements, an |
| | | ambiguity arises. Views registered for the resource class take precedence over |
| | | any views registered for any interface the resource class implements. Thus, if |
| | | one view configuration names a ``context`` of both the class type of a |
| | | resource, and another view configuration names a ``context`` of interface |
| | | implemented by the resource's class, and both view configurations are otherwise |
| | | identical, the view registered for the context's class will "win". |
| | | |
| | | For more information about defining resources with interfaces for use within |
| | | view configuration, see :ref:`resources_which_implement_interfaces`. |
| | |
| | | |
| | | The :meth:`pyramid.request.Request.resource_url` method generates a URL when |
| | | given a resource retrieved from a resource tree. |
| | | |
| | |
| | | .. sidebar:: About Release Numbering |
| | | |
| | | Conventionally, application version numbering in Python is described as |
| | | ``major.minor.micro``. If your Pyramid version is "1.2.3", it means |
| | | you're running a version of Pyramid with the major version "1", the minor |
| | | version "2" and the micro version "3". A "major" release is one that |
| | | increments the first-dot number; 2.X.X might follow 1.X.X. A "minor" |
| | | release is one that increments the second-dot number; 1.3.X might follow |
| | | 1.2.X. A "micro" release is one that increments the third-dot number; |
| | | 1.2.3 might follow 1.2.2. In general, micro releases are "bugfix-only", |
| | | and contain no new features, minor releases contain new features but are |
| | | largely backwards compatible with older versions, and a major release |
| | | indicates a large set of backwards incompatibilities. |
| | | ``major.minor.micro``. If your Pyramid version is "1.2.3", it means you're |
| | | running a version of Pyramid with the major version "1", the minor version |
| | | "2" and the micro version "3". A "major" release is one that increments the |
| | | first-dot number; 2.X.X might follow 1.X.X. A "minor" release is one that |
| | | increments the second-dot number; 1.3.X might follow 1.2.X. A "micro" |
| | | release is one that increments the third-dot number; 1.2.3 might follow |
| | | 1.2.2. In general, micro releases are "bugfix-only", and contain no new |
| | | features, minor releases contain new features but are largely backwards |
| | | compatible with older versions, and a major release indicates a large set of |
| | | backwards incompatibilities. |
| | | |
| | | The Pyramid core team is conservative when it comes to removing features. We |
| | | don't remove features unnecessarily, but we're human, and we make mistakes |
| | | which cause some features to be evolutionary dead ends. Though we are |
| | | willing to support dead-end features for some amount of time, some eventually |
| | | have to be removed when the cost of supporting them outweighs the benefit of |
| | | keeping them around, because each feature in Pyramid represents a certain |
| | | documentation and maintenance burden. |
| | | don't remove features unnecessarily, but we're human and we make mistakes which |
| | | cause some features to be evolutionary dead ends. Though we are willing to |
| | | support dead-end features for some amount of time, some eventually have to be |
| | | removed when the cost of supporting them outweighs the benefit of keeping them |
| | | around, because each feature in Pyramid represents a certain documentation and |
| | | maintenance burden. |
| | | |
| | | Deprecation and Removal Policy |
| | | Deprecation and removal policy |
| | | ------------------------------ |
| | | |
| | | When a feature is scheduled for removal from Pyramid or any of its official |
| | |
| | | |
| | | - A note is added to the :ref:`changelog` about the removal. |
| | | |
| | | Features are never removed in *micro* releases. They are only removed in |
| | | minor and major releases. Deprecated features are kept around for at least |
| | | *three* minor releases from the time the feature became deprecated. |
| | | Therefore, if a feature is added in Pyramid 1.0, but it's deprecated in |
| | | Pyramid 1.1, it will be kept around through all 1.1.X releases, all 1.2.X |
| | | releases and all 1.3.X releases. It will finally be removed in the first |
| | | 1.4.X release. |
| | | Features are never removed in *micro* releases. They are only removed in minor |
| | | and major releases. Deprecated features are kept around for at least *three* |
| | | minor releases from the time the feature became deprecated. Therefore, if a |
| | | feature is added in Pyramid 1.0, but it's deprecated in Pyramid 1.1, it will be |
| | | kept around through all 1.1.X releases, all 1.2.X releases and all 1.3.X |
| | | releases. It will finally be removed in the first 1.4.X release. |
| | | |
| | | Sometimes features are "docs-deprecated" instead of formally deprecated. |
| | | This means that the feature will be kept around indefinitely, but it will be |
| | | removed from the documentation or a note will be added to the documentation |
| | | telling folks to use some other newer feature. This happens when the cost of |
| | | keeping an old feature around is very minimal and the support and |
| | | documentation burden is very low. For example, we might rename a function |
| | | that is an API without changing the arguments it accepts. In this case, |
| | | we'll often rename the function, and change the docs to point at the new |
| | | function name, but leave around a backwards compatibility alias to the old |
| | | function name so older code doesn't break. |
| | | Sometimes features are "docs-deprecated" instead of formally deprecated. This |
| | | means that the feature will be kept around indefinitely, but it will be removed |
| | | from the documentation or a note will be added to the documentation telling |
| | | folks to use some other newer feature. This happens when the cost of keeping |
| | | an old feature around is very minimal and the support and documentation burden |
| | | is very low. For example, we might rename a function that is an API without |
| | | changing the arguments it accepts. In this case, we'll often rename the |
| | | function, and change the docs to point at the new function name, but leave |
| | | around a backwards compatibility alias to the old function name so older code |
| | | doesn't break. |
| | | |
| | | "Docs deprecated" features tend to work "forever", meaning that they won't be |
| | | removed, and they'll never generate a deprecation warning. However, such |
| | | changes are noted in the :ref:`changelog`, so it's possible to know that you |
| | | should change older spellings to newer ones to ensure that people reading |
| | | your code can find the APIs you're using in the Pyramid docs. |
| | | should change older spellings to newer ones to ensure that people reading your |
| | | code can find the APIs you're using in the Pyramid docs. |
| | | |
| | | Consulting the Change History |
| | | Consulting the change history |
| | | ----------------------------- |
| | | |
| | | Your first line of defense against application failures caused by upgrading |
| | | to a newer Pyramid release is always to read the :ref:`changelog`. to find |
| | | the deprecations and removals for each release between the release you're |
| | | currently running and the one you wish to upgrade to. The change history |
| | | notes every deprecation within a ``Deprecation`` section and every removal |
| | | within a ``Backwards Incompatibilies`` section for each release. |
| | | Your first line of defense against application failures caused by upgrading to |
| | | a newer Pyramid release is always to read the :ref:`changelog` to find the |
| | | deprecations and removals for each release between the release you're currently |
| | | running and the one to which you wish to upgrade. The change history notes |
| | | every deprecation within a ``Deprecation`` section and every removal within a |
| | | ``Backwards Incompatibilies`` section for each release. |
| | | |
| | | The change history often contains instructions for changing your code to |
| | | avoid deprecation warnings and how to change docs-deprecated spellings to |
| | | newer ones. You can follow along with each deprecation explanation in the |
| | | change history, simply doing a grep or other code search to your application, |
| | | using the change log examples to remediate each potential problem. |
| | | The change history often contains instructions for changing your code to avoid |
| | | deprecation warnings and how to change docs-deprecated spellings to newer ones. |
| | | You can follow along with each deprecation explanation in the change history, |
| | | simply doing a grep or other code search to your application, using the change |
| | | log examples to remediate each potential problem. |
| | | |
| | | .. _testing_under_new_release: |
| | | |
| | | Testing Your Application Under a New Pyramid Release |
| | | Testing your application under a new Pyramid release |
| | | ---------------------------------------------------- |
| | | |
| | | Once you've upgraded your application to a new Pyramid release and you've |
| | |
| | | |
| | | $ python -Wd setup.py test -q |
| | | |
| | | The ``-Wd`` argument is an argument that tells Python to print deprecation |
| | | warnings to the console. Note that the ``-Wd`` flag is only required for |
| | | Python 2.7 and better: Python versions 2.6 and older print deprecation |
| | | warnings to the console by default. See `the Python -W flag documentation |
| | | <http://docs.python.org/using/cmdline.html#cmdoption-W>`_ for more |
| | | information. |
| | | The ``-Wd`` argument tells Python to print deprecation warnings to the console. |
| | | Note that the ``-Wd`` flag is only required for Python 2.7 and better: Python |
| | | versions 2.6 and older print deprecation warnings to the console by default. |
| | | See `the Python -W flag documentation |
| | | <http://docs.python.org/using/cmdline.html#cmdoption-W>`_ for more information. |
| | | |
| | | As your tests run, deprecation warnings will be printed to the console |
| | | explaining the deprecation and providing instructions about how to prevent |
| | | the deprecation warning from being issued. For example: |
| | | explaining the deprecation and providing instructions about how to prevent the |
| | | deprecation warning from being issued. For example: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ python -Wd setup.py test -q |
| | | # .. elided ... |
| | | running build_ext |
| | | /home/chrism/projects/pyramid/env27/myproj/myproj/views.py:3: |
| | | DeprecationWarning: static: The "pyramid.view.static" class is deprecated |
| | | as of Pyramid 1.1; use the "pyramid.static.static_view" class instead with |
| | | /home/chrism/projects/pyramid/env27/myproj/myproj/views.py:3: |
| | | DeprecationWarning: static: The "pyramid.view.static" class is deprecated |
| | | as of Pyramid 1.1; use the "pyramid.static.static_view" class instead with |
| | | the "use_subpath" argument set to True. |
| | | from pyramid.view import static |
| | | . |
| | |
| | | from pyramid.view import static |
| | | myview = static('static', 'static') |
| | | |
| | | The deprecation warning tells me how to fix it, so I can change the code to |
| | | do things the newer way: |
| | | The deprecation warning tells me how to fix it, so I can change the code to do |
| | | things the newer way: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | from pyramid.static import static_view |
| | | myview = static_view('static', 'static', use_subpath=True) |
| | | |
| | | When I run the tests again, the deprecation warning is no longer printed to |
| | | my console: |
| | | When I run the tests again, the deprecation warning is no longer printed to my |
| | | console: |
| | | |
| | | .. code-block:: text |
| | | .. code-block:: bash |
| | | |
| | | $ python -Wd setup.py test -q |
| | | # .. elided ... |
| | |
| | | OK |
| | | |
| | | |
| | | My Application Doesn't Have Any Tests or Has Few Tests |
| | | My application doesn't have any tests or has few tests |
| | | ------------------------------------------------------ |
| | | |
| | | If your application has no tests, or has only moderate test coverage, running |
| | |
| | | deprecation warnings won't be executed. |
| | | |
| | | In this circumstance, you can start your application interactively under a |
| | | server run with the ``PYTHONWARNINGS`` environment variable set to |
| | | ``default``. On UNIX, you can do that via: |
| | | server run with the ``PYTHONWARNINGS`` environment variable set to ``default``. |
| | | On UNIX, you can do that via: |
| | | |
| | | .. code-block:: bash |
| | | |
| | |
| | | |
| | | At this point, it's ensured that deprecation warnings will be printed to the |
| | | console whenever a codepath is hit that generates one. You can then click |
| | | around in your application interactively to try to generate them, and |
| | | remediate as explained in :ref:`testing_under_new_release`. |
| | | around in your application interactively to try to generate them, and remediate |
| | | as explained in :ref:`testing_under_new_release`. |
| | | |
| | | See `the PYTHONWARNINGS environment variable documentation |
| | | <http://docs.python.org/using/cmdline.html#envvar-PYTHONWARNINGS>`_ or `the |
| | | Python -W flag documentation |
| | | <http://docs.python.org/using/cmdline.html#cmdoption-W>`_ for more |
| | | information. |
| | | <http://docs.python.org/using/cmdline.html#cmdoption-W>`_ for more information. |
| | | |
| | | Upgrading to the Very Latest Pyramid Release |
| | | Upgrading to the very latest Pyramid release |
| | | -------------------------------------------- |
| | | |
| | | When you upgrade your application to the most recent Pyramid release, |
| | |
| | | :ref:`testing_under_new_release`. Note any deprecation warnings and |
| | | remediate. |
| | | |
| | | - Upgrade to the most recent 1.3 release, 1.3.3. Run your application's |
| | | tests, note any deprecation warnings and remediate. |
| | | - Upgrade to the most recent 1.3 release, 1.3.3. Run your application's tests, |
| | | note any deprecation warnings, and remediate. |
| | | |
| | | - Upgrade to 1.4.4. Run your application's tests, note any deprecation |
| | | warnings and remediate. |
| | | warnings, and remediate. |
| | | |
| | | If you skip testing your application under each minor release (for example if |
| | | you upgrade directly from 1.2.1 to 1.4.4), you might miss a deprecation |
| | | warning and waste more time trying to figure out an error caused by a feature |
| | | removal than it would take to upgrade stepwise through each minor release. |
| | | |
| | | |
| | | you upgrade directly from 1.2.1 to 1.4.4), you might miss a deprecation warning |
| | | and waste more time trying to figure out an error caused by a feature removal |
| | | than it would take to upgrade stepwise through each minor release. |
| | |
| | | .. _zca_chapter: |
| | | |
| | | Using the Zope Component Architecture in :app:`Pyramid` |
| | | ========================================================== |
| | | ======================================================= |
| | | |
| | | Under the hood, :app:`Pyramid` uses a :term:`Zope Component |
| | | Architecture` component registry as its :term:`application registry`. |
| | | The Zope Component Architecture is referred to colloquially as the |
| | | "ZCA." |
| | | Under the hood, :app:`Pyramid` uses a :term:`Zope Component Architecture` |
| | | component registry as its :term:`application registry`. The Zope Component |
| | | Architecture is referred to colloquially as the "ZCA." |
| | | |
| | | The ``zope.component`` API used to access data in a traditional Zope |
| | | application can be opaque. For example, here is a typical "unnamed |
| | | utility" lookup using the :func:`zope.component.getUtility` global API |
| | | as it might appear in a traditional Zope application: |
| | | application can be opaque. For example, here is a typical "unnamed utility" |
| | | lookup using the :func:`zope.component.getUtility` global API as it might |
| | | appear in a traditional Zope application: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | from zope.component import getUtility |
| | | settings = getUtility(ISettings) |
| | | |
| | | After this code runs, ``settings`` will be a Python dictionary. But |
| | | it's unlikely that any "civilian" will be able to figure this out just |
| | | by reading the code casually. When the ``zope.component.getUtility`` |
| | | API is used by a developer, the conceptual load on a casual reader of |
| | | code is high. |
| | | After this code runs, ``settings`` will be a Python dictionary. But it's |
| | | unlikely that any "civilian" will be able to figure this out just by reading |
| | | the code casually. When the ``zope.component.getUtility`` API is used by a |
| | | developer, the conceptual load on a casual reader of code is high. |
| | | |
| | | While the ZCA is an excellent tool with which to build a *framework* |
| | | such as :app:`Pyramid`, it is not always the best tool with which |
| | | to build an *application* due to the opacity of the ``zope.component`` |
| | | APIs. Accordingly, :app:`Pyramid` tends to hide the presence of the |
| | | ZCA from application developers. You needn't understand the ZCA to |
| | | create a :app:`Pyramid` application; its use is effectively only a |
| | | framework implementation detail. |
| | | While the ZCA is an excellent tool with which to build a *framework* such as |
| | | :app:`Pyramid`, it is not always the best tool with which to build an |
| | | *application* due to the opacity of the ``zope.component`` APIs. Accordingly, |
| | | :app:`Pyramid` tends to hide the presence of the ZCA from application |
| | | developers. You needn't understand the ZCA to create a :app:`Pyramid` |
| | | application; its use is effectively only a framework implementation detail. |
| | | |
| | | However, developers who are already used to writing :term:`Zope` |
| | | applications often still wish to use the ZCA while building a |
| | | :app:`Pyramid` application; :app:`Pyramid` makes this possible. |
| | | However, developers who are already used to writing :term:`Zope` applications |
| | | often still wish to use the ZCA while building a :app:`Pyramid` application. |
| | | :app:`Pyramid` makes this possible. |
| | | |
| | | .. index:: |
| | | single: get_current_registry |
| | |
| | | single: getSiteManager |
| | | single: ZCA global API |
| | | |
| | | Using the ZCA Global API in a :app:`Pyramid` Application |
| | | ----------------------------------------------------------- |
| | | Using the ZCA global API in a :app:`Pyramid` application |
| | | -------------------------------------------------------- |
| | | |
| | | :term:`Zope` uses a single ZCA registry -- the "global" ZCA registry |
| | | -- for all Zope applications that run in the same Python process, |
| | | effectively making it impossible to run more than one Zope application |
| | | in a single process. |
| | | :term:`Zope` uses a single ZCA registry—the "global" ZCA registry—for all Zope |
| | | applications that run in the same Python process, effectively making it |
| | | impossible to run more than one Zope application in a single process. |
| | | |
| | | However, for ease of deployment, it's often useful to be able to run more |
| | | than a single application per process. For example, use of a |
| | | :term:`PasteDeploy` "composite" allows you to run separate individual WSGI |
| | | applications in the same process, each answering requests for some URL |
| | | prefix. This makes it possible to run, for example, a TurboGears application |
| | | at ``/turbogears`` and a :app:`Pyramid` application at ``/pyramid``, both |
| | | served up using the same :term:`WSGI` server within a single Python process. |
| | | However, for ease of deployment, it's often useful to be able to run more than |
| | | a single application per process. For example, use of a :term:`PasteDeploy` |
| | | "composite" allows you to run separate individual WSGI applications in the same |
| | | process, each answering requests for some URL prefix. This makes it possible |
| | | to run, for example, a TurboGears application at ``/turbogears`` and a |
| | | :app:`Pyramid` application at ``/pyramid``, both served up using the same |
| | | :term:`WSGI` server within a single Python process. |
| | | |
| | | Most production Zope applications are relatively large, making it |
| | | impractical due to memory constraints to run more than one Zope |
| | | application per Python process. However, a :app:`Pyramid` application |
| | | may be very small and consume very little memory, so it's a reasonable |
| | | goal to be able to run more than one :app:`Pyramid` application per |
| | | process. |
| | | Most production Zope applications are relatively large, making it impractical |
| | | due to memory constraints to run more than one Zope application per Python |
| | | process. However, a :app:`Pyramid` application may be very small and consume |
| | | very little memory, so it's a reasonable goal to be able to run more than one |
| | | :app:`Pyramid` application per process. |
| | | |
| | | In order to make it possible to run more than one :app:`Pyramid` |
| | | application in a single process, :app:`Pyramid` defaults to using a |
| | | separate ZCA registry *per application*. |
| | | In order to make it possible to run more than one :app:`Pyramid` application in |
| | | a single process, :app:`Pyramid` defaults to using a separate ZCA registry *per |
| | | application*. |
| | | |
| | | While this services a reasonable goal, it causes some issues when |
| | | trying to use patterns which you might use to build a typical |
| | | :term:`Zope` application to build a :app:`Pyramid` application. |
| | | Without special help, ZCA "global" APIs such as |
| | | :func:`zope.component.getUtility` and :func:`zope.component.getSiteManager` |
| | | will use the ZCA "global" registry. Therefore, these APIs |
| | | will appear to fail when used in a :app:`Pyramid` application, |
| | | because they'll be consulting the ZCA global registry rather than the |
| | | component registry associated with your :app:`Pyramid` application. |
| | | While this services a reasonable goal, it causes some issues when trying to use |
| | | patterns which you might use to build a typical :term:`Zope` application to |
| | | build a :app:`Pyramid` application. Without special help, ZCA "global" APIs |
| | | such as :func:`zope.component.getUtility` and |
| | | :func:`zope.component.getSiteManager` will use the ZCA "global" registry. |
| | | Therefore, these APIs will appear to fail when used in a :app:`Pyramid` |
| | | application, because they'll be consulting the ZCA global registry rather than |
| | | the component registry associated with your :app:`Pyramid` application. |
| | | |
| | | There are three ways to fix this: by disusing the ZCA global API |
| | | entirely, by using |
| | | :meth:`pyramid.config.Configurator.hook_zca` or by passing |
| | | the ZCA global registry to the :term:`Configurator` constructor at |
| | | startup time. We'll describe all three methods in this section. |
| | | There are three ways to fix this: by disusing the ZCA global API entirely, by |
| | | using :meth:`pyramid.config.Configurator.hook_zca` or by passing the ZCA global |
| | | registry to the :term:`Configurator` constructor at startup time. We'll |
| | | describe all three methods in this section. |
| | | |
| | | .. index:: |
| | | single: request.registry |
| | | |
| | | .. _disusing_the_global_zca_api: |
| | | |
| | | Disusing the Global ZCA API |
| | | Disusing the global ZCA API |
| | | +++++++++++++++++++++++++++ |
| | | |
| | | ZCA "global" API functions such as ``zope.component.getSiteManager``, |
| | | ``zope.component.getUtility``, :func:`zope.component.getAdapter`, and |
| | | :func:`zope.component.getMultiAdapter` aren't strictly necessary. Every |
| | | component registry has a method API that offers the same |
| | | functionality; it can be used instead. For example, presuming the |
| | | ``registry`` value below is a Zope Component Architecture component |
| | | registry, the following bit of code is equivalent to |
| | | ``zope.component.getUtility(IFoo)``: |
| | | component registry has a method API that offers the same functionality; it can |
| | | be used instead. For example, presuming the ``registry`` value below is a Zope |
| | | Component Architecture component registry, the following bit of code is |
| | | equivalent to ``zope.component.getUtility(IFoo)``: |
| | | |
| | | .. code-block:: python |
| | | |
| | | registry.getUtility(IFoo) |
| | | |
| | | The full method API is documented in the ``zope.component`` package, |
| | | but it largely mirrors the "global" API almost exactly. |
| | | The full method API is documented in the ``zope.component`` package, but it |
| | | largely mirrors the "global" API almost exactly. |
| | | |
| | | If you are willing to disuse the "global" ZCA APIs and use the method |
| | | interface of a registry instead, you need only know how to obtain the |
| | | :app:`Pyramid` component registry. |
| | | If you are willing to disuse the "global" ZCA APIs and use the method interface |
| | | of a registry instead, you need only know how to obtain the :app:`Pyramid` |
| | | component registry. |
| | | |
| | | There are two ways of doing so: |
| | | |
| | | - use the :func:`pyramid.threadlocal.get_current_registry` |
| | | function within :app:`Pyramid` view or resource code. This will |
| | | always return the "current" :app:`Pyramid` application registry. |
| | | - use the :func:`pyramid.threadlocal.get_current_registry` function within |
| | | :app:`Pyramid` view or resource code. This will always return the "current" |
| | | :app:`Pyramid` application registry. |
| | | |
| | | - use the attribute of the :term:`request` object named ``registry`` |
| | | in your :app:`Pyramid` view code, eg. ``request.registry``. This |
| | | is the ZCA component registry related to the running |
| | | :app:`Pyramid` application. |
| | | - use the attribute of the :term:`request` object named ``registry`` in your |
| | | :app:`Pyramid` view code, e.g., ``request.registry``. This is the ZCA |
| | | component registry related to the running :app:`Pyramid` application. |
| | | |
| | | See :ref:`threadlocals_chapter` for more information about |
| | | :func:`pyramid.threadlocal.get_current_registry`. |
| | |
| | | |
| | | .. _hook_zca: |
| | | |
| | | Enabling the ZCA Global API by Using ``hook_zca`` |
| | | Enabling the ZCA global API by using ``hook_zca`` |
| | | +++++++++++++++++++++++++++++++++++++++++++++++++ |
| | | |
| | | Consider the following bit of idiomatic :app:`Pyramid` startup code: |
| | |
| | | config.include('some.other.package') |
| | | return config.make_wsgi_app() |
| | | |
| | | When the ``app`` function above is run, a :term:`Configurator` is |
| | | constructed. When the configurator is created, it creates a *new* |
| | | :term:`application registry` (a ZCA component registry). A new |
| | | registry is constructed whenever the ``registry`` argument is omitted |
| | | when a :term:`Configurator` constructor is called, or when a |
| | | ``registry`` argument with a value of ``None`` is passed to a |
| | | :term:`Configurator` constructor. |
| | | When the ``app`` function above is run, a :term:`Configurator` is constructed. |
| | | When the configurator is created, it creates a *new* :term:`application |
| | | registry` (a ZCA component registry). A new registry is constructed whenever |
| | | the ``registry`` argument is omitted, when a :term:`Configurator` constructor |
| | | is called, or when a ``registry`` argument with a value of ``None`` is passed |
| | | to a :term:`Configurator` constructor. |
| | | |
| | | During a request, the application registry created by the Configurator |
| | | is "made current". This means calls to |
| | | :func:`~pyramid.threadlocal.get_current_registry` in the thread |
| | | handling the request will return the component registry associated |
| | | with the application. |
| | | During a request, the application registry created by the Configurator is "made |
| | | current". This means calls to |
| | | :func:`~pyramid.threadlocal.get_current_registry` in the thread handling the |
| | | request will return the component registry associated with the application. |
| | | |
| | | As a result, application developers can use ``get_current_registry`` |
| | | to get the registry and thus get access to utilities and such, as per |
| | | :ref:`disusing_the_global_zca_api`. But they still cannot use the |
| | | global ZCA API. Without special treatment, the ZCA global APIs will |
| | | always return the global ZCA registry (the one in |
| | | ``zope.component.globalregistry.base``). |
| | | As a result, application developers can use ``get_current_registry`` to get the |
| | | registry and thus get access to utilities and such, as per |
| | | :ref:`disusing_the_global_zca_api`. But they still cannot use the global ZCA |
| | | API. Without special treatment, the ZCA global APIs will always return the |
| | | global ZCA registry (the one in ``zope.component.globalregistry.base``). |
| | | |
| | | To "fix" this and make the ZCA global APIs use the "current" |
| | | :app:`Pyramid` registry, you need to call |
| | | :meth:`~pyramid.config.Configurator.hook_zca` within your setup code. |
| | | For example: |
| | | To "fix" this and make the ZCA global APIs use the "current" :app:`Pyramid` |
| | | registry, you need to call :meth:`~pyramid.config.Configurator.hook_zca` within |
| | | your setup code. For example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | :emphasize-lines: 5 |
| | | |
| | | from pyramid.config import Configurator |
| | | |
| | |
| | | config.include('some.other.application') |
| | | return config.make_wsgi_app() |
| | | |
| | | We've added a line to our original startup code, line number 6, which |
| | | calls ``config.hook_zca()``. The effect of this line under the hood |
| | | is that an analogue of the following code is executed: |
| | | We've added a line to our original startup code, line number 5, which calls |
| | | ``config.hook_zca()``. The effect of this line under the hood is that an |
| | | analogue of the following code is executed: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | from pyramid.threadlocal import get_current_registry |
| | | getSiteManager.sethook(get_current_registry) |
| | | |
| | | This causes the ZCA global API to start using the :app:`Pyramid` |
| | | application registry in threads which are running a :app:`Pyramid` |
| | | request. |
| | | This causes the ZCA global API to start using the :app:`Pyramid` application |
| | | registry in threads which are running a :app:`Pyramid` request. |
| | | |
| | | Calling ``hook_zca`` is usually sufficient to "fix" the problem of |
| | | being able to use the global ZCA API within a :app:`Pyramid` |
| | | application. However, it also means that a Zope application that is |
| | | running in the same process may start using the :app:`Pyramid` |
| | | global registry instead of the Zope global registry, effectively |
| | | inverting the original problem. In such a case, follow the steps in |
| | | the next section, :ref:`using_the_zca_global_registry`. |
| | | Calling ``hook_zca`` is usually sufficient to "fix" the problem of being able |
| | | to use the global ZCA API within a :app:`Pyramid` application. However, it |
| | | also means that a Zope application that is running in the same process may |
| | | start using the :app:`Pyramid` global registry instead of the Zope global |
| | | registry, effectively inverting the original problem. In such a case, follow |
| | | the steps in the next section, :ref:`using_the_zca_global_registry`. |
| | | |
| | | .. index:: |
| | | single: get_current_registry |
| | |
| | | |
| | | .. _using_the_zca_global_registry: |
| | | |
| | | Enabling the ZCA Global API by Using The ZCA Global Registry |
| | | Enabling the ZCA global API by using the ZCA global registry |
| | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| | | |
| | | You can tell your :app:`Pyramid` application to use the ZCA global |
| | | registry at startup time instead of constructing a new one: |
| | | You can tell your :app:`Pyramid` application to use the ZCA global registry at |
| | | startup time instead of constructing a new one: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | :emphasize-lines: 5-7 |
| | | |
| | | from zope.component import getGlobalSiteManager |
| | | from pyramid.config import Configurator |
| | |
| | | config.include('some.other.application') |
| | | return config.make_wsgi_app() |
| | | |
| | | Lines 5, 6, and 7 above are the interesting ones. Line 5 retrieves |
| | | the global ZCA component registry. Line 6 creates a |
| | | :term:`Configurator`, passing the global ZCA registry into its |
| | | constructor as the ``registry`` argument. Line 7 "sets up" the global |
| | | registry with Pyramid-specific registrations; this is code that is |
| | | normally executed when a registry is constructed rather than created, |
| | | Lines 5, 6, and 7 above are the interesting ones. Line 5 retrieves the global |
| | | ZCA component registry. Line 6 creates a :term:`Configurator`, passing the |
| | | global ZCA registry into its constructor as the ``registry`` argument. Line 7 |
| | | "sets up" the global registry with Pyramid-specific registrations; this is code |
| | | that is normally executed when a registry is constructed rather than created, |
| | | but we must call it "by hand" when we pass an explicit registry. |
| | | |
| | | At this point, :app:`Pyramid` will use the ZCA global registry |
| | | rather than creating a new application-specific registry; since by |
| | | default the ZCA global API will use this registry, things will work as |
| | | you might expect a Zope app to when you use the global ZCA API. |
| | | |
| | | At this point, :app:`Pyramid` will use the ZCA global registry rather than |
| | | creating a new application-specific registry. Since by default the ZCA global |
| | | API will use this registry, things will work as you might expect in a Zope app |
| | | when you use the global ZCA API. |
New file |
| | |
| | | .. _pscripts_documentation: |
| | | |
| | | ``p*`` Scripts Documentation |
| | | ============================ |
| | | |
| | | Command line programs (``p*`` scripts) included with :app:`Pyramid`. |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | :glob: |
| | | |
| | | * |
New file |
| | |
| | | .. index:: |
| | | single: pcreate; --help |
| | | |
| | | .. _pcreate_script: |
| | | |
| | | ``pcreate`` |
| | | ----------- |
| | | |
| | | .. program-output:: pcreate --help |
| | | :prompt: |
| | | :shell: |
| | | |
| | | .. seealso:: :ref:`creating_a_project` |
New file |
| | |
| | | .. index:: |
| | | single: pdistreport; --help |
| | | |
| | | .. _pdistreport_script: |
| | | |
| | | ``pdistreport`` |
| | | --------------- |
| | | |
| | | .. program-output:: pdistreport --help |
| | | :prompt: |
| | | :shell: |
| | | |
| | | .. seealso:: :ref:`showing_distributions` |
New file |
| | |
| | | .. index:: |
| | | single: prequest; --help |
| | | |
| | | .. _prequest_script: |
| | | |
| | | ``prequest`` |
| | | ------------ |
| | | |
| | | .. program-output:: prequest --help |
| | | :prompt: |
| | | :shell: |
| | | |
| | | .. seealso:: :ref:`invoking_a_request` |
New file |
| | |
| | | .. index:: |
| | | single: proutes; --help |
| | | |
| | | .. _proutes_script: |
| | | |
| | | ``proutes`` |
| | | ----------- |
| | | |
| | | .. program-output:: proutes --help |
| | | :prompt: |
| | | :shell: |
| | | |
| | | .. seealso:: :ref:`displaying_application_routes` |
New file |
| | |
| | | .. index:: |
| | | single: pserve; --help |
| | | |
| | | .. _pserve_script: |
| | | |
| | | ``pserve`` |
| | | ---------- |
| | | |
| | | .. program-output:: pserve --help |
| | | :prompt: |
| | | :shell: |
| | | |
| | | .. seealso:: :ref:`running_the_project_application` |
New file |
| | |
| | | .. index:: |
| | | single: pshell; --help |
| | | |
| | | .. _pshell_script: |
| | | |
| | | ``pshell`` |
| | | ---------- |
| | | |
| | | .. program-output:: pshell --help |
| | | :prompt: |
| | | :shell: |
| | | |
| | | .. seealso:: :ref:`interactive_shell` |
New file |
| | |
| | | .. index:: |
| | | single: ptweens; --help |
| | | |
| | | .. _ptweens_script: |
| | | |
| | | ``ptweens`` |
| | | ----------- |
| | | |
| | | .. program-output:: ptweens --help |
| | | :prompt: |
| | | :shell: |
| | | |
| | | .. seealso:: :ref:`displaying_tweens` |
New file |
| | |
| | | .. index:: |
| | | single: pviews; --help |
| | | |
| | | .. _pviews_script: |
| | | |
| | | ``pviews`` |
| | | ---------- |
| | | |
| | | .. program-output:: pviews --help |
| | | :prompt: |
| | | :shell: |
| | | |
| | | .. seealso:: :ref:`displaying_matching_views` |
| | |
| | | base class for our model: |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/models.py |
| | | :lines: 17 |
| | | :lines: 18 |
| | | :language: py |
| | | |
| | | Our model classes will inherit from this ``Base`` class so they can be |
| | |
| | | What's New In Pyramid 1.0 |
| | | What's New in Pyramid 1.0 |
| | | ========================= |
| | | |
| | | This article explains the new features in Pyramid version 1.0 as compared to |
| | |
| | | What's New In Pyramid 1.1 |
| | | What's New in Pyramid 1.1 |
| | | ========================= |
| | | |
| | | This article explains the new features in Pyramid version 1.1 as compared to |
| | |
| | | What's New In Pyramid 1.2 |
| | | What's New in Pyramid 1.2 |
| | | ========================= |
| | | |
| | | This article explains the new features in :app:`Pyramid` version 1.2 as |
| | |
| | | What's New In Pyramid 1.3 |
| | | What's New in Pyramid 1.3 |
| | | ========================= |
| | | |
| | | This article explains the new features in :app:`Pyramid` version 1.3 as |
| | |
| | | What's New In Pyramid 1.4 |
| | | What's New in Pyramid 1.4 |
| | | ========================= |
| | | |
| | | This article explains the new features in :app:`Pyramid` version 1.4 as |
| | |
| | | What's New In Pyramid 1.5 |
| | | What's New in Pyramid 1.5 |
| | | ========================= |
| | | |
| | | This article explains the new features in :app:`Pyramid` version 1.5 as |
| | |
| | | |
| | | The feature additions in Pyramid 1.5 follow. |
| | | |
| | | - Python 3.4 compatibility. |
| | | |
| | | - Add ``pdistreport`` script, which prints the Python version in use, the |
| | | Pyramid version in use, and the version number and location of all Python |
| | | distributions currently installed. |
| | |
| | | What's New In Pyramid 1.6 |
| | | What's New in Pyramid 1.6 |
| | | ========================= |
| | | |
| | | This article explains the new features in :app:`Pyramid` version 1.6 as |
| | | compared to its predecessor, :app:`Pyramid` 1.5. It also documents backwards |
| | | compared to its predecessor, :app:`Pyramid` 1.5. It also documents backwards |
| | | incompatibilities between the two versions and deprecations added to |
| | | :app:`Pyramid` 1.6, as well as software dependency changes and notable |
| | | documentation additions. |
| | | |
| | | |
| | | Backwards Incompatibilities |
| | | --------------------------- |
| | | |
| | | - IPython and BPython support have been removed from pshell in the core. To |
| | | continue using them on Pyramid 1.6+, you must install the binding packages |
| | | explicitly. One way to do this is by adding ``pyramid_ipython`` (or |
| | | ``pyramid_bpython``) to the ``install_requires`` section of your package's |
| | | ``setup.py`` file, then re-running ``setup.py develop``:: |
| | | |
| | | setup( |
| | | #... |
| | | install_requires=[ |
| | | 'pyramid_ipython', # new dependency |
| | | 'pyramid', |
| | | #... |
| | | ], |
| | | ) |
| | | |
| | | - ``request.response`` will no longer be mutated when using the |
| | | :func:`~pyramid.renderers.render_to_response` API. It is now necessary |
| | | to pass in |
| | | a ``response=`` argument to :func:`~pyramid.renderers.render_to_response` if |
| | | you wish to supply the renderer with a custom response object for it to |
| | | use. If you do not pass one then a response object will be created using the |
| | | current response factory. Almost all renderers mutate the |
| | | ``request.response`` response object (for example, the JSON renderer sets |
| | | ``request.response.content_type`` to ``application/json``). However, when |
| | | invoking ``render_to_response`` it is not expected that the response object |
| | | being returned would be the same one used later in the request. The response |
| | | object returned from ``render_to_response`` is now explicitly different from |
| | | ``request.response``. This does not change the API of a renderer. See |
| | | :func:`~pyramid.renderers.render_to_response` API. It is now necessary to |
| | | pass in a ``response=`` argument to |
| | | :func:`~pyramid.renderers.render_to_response` if you wish to supply the |
| | | renderer with a custom response object. If you do not pass one, then a |
| | | response object will be created using the current response factory. Almost |
| | | all renderers mutate the ``request.response`` response object (for example, |
| | | the JSON renderer sets ``request.response.content_type`` to |
| | | ``application/json``). However, when invoking ``render_to_response``, it is |
| | | not expected that the response object being returned would be the same one |
| | | used later in the request. The response object returned from |
| | | ``render_to_response`` is now explicitly different from ``request.response``. |
| | | This does not change the API of a renderer. See |
| | | https://github.com/Pylons/pyramid/pull/1563 |
| | | |
| | | |
| | | Feature Additions |
| | | ----------------- |
| | | |
| | | - Cache busting for static assets has been added and is available via a new |
| | | argument to :meth:`pyramid.config.Configurator.add_static_view`: |
| | | ``cachebust``. Core APIs are shipped for both cache busting via query |
| | | strings and path segments and may be extended to fit into custom asset |
| | | pipelines. See https://github.com/Pylons/pyramid/pull/1380 and |
| | | https://github.com/Pylons/pyramid/pull/1583 |
| | | - Python 3.5 and pypy3 compatibility. |
| | | |
| | | - ``pserve --reload`` will no longer crash on syntax errors. See |
| | | https://github.com/Pylons/pyramid/pull/2044 |
| | | |
| | | - Cache busting for static resources has been added and is available via a new |
| | | :meth:`pyramid.config.Configurator.add_cache_buster` API. Core APIs are |
| | | shipped for both cache busting via query strings and via asset manifests for |
| | | integrating into custom asset pipelines. See |
| | | https://github.com/Pylons/pyramid/pull/1380 and |
| | | https://github.com/Pylons/pyramid/pull/1583 and |
| | | https://github.com/Pylons/pyramid/pull/2171 |
| | | |
| | | - Assets can now be overidden by an absolute path on the filesystem when using |
| | | the :meth:`~pyramid.config.Configurator.override_asset` API. This makes it |
| | |
| | | ``config.add_static_view('myapp:static', 'static')`` and |
| | | ``config.override_asset(to_override='myapp:static/', |
| | | override_with='/abs/path/')``. The ``myapp:static`` asset spec is completely |
| | | made up and does not need to exist - it is used for generating urls via |
| | | ``request.static_url('myapp:static/foo.png')``. See |
| | | made up and does not need to exist—it is used for generating URLs via |
| | | ``request.static_url('myapp:static/foo.png')``. See |
| | | https://github.com/Pylons/pyramid/issues/1252 |
| | | |
| | | - Added :meth:`~pyramid.config.Configurator.set_response_factory` and the |
| | | ``response_factory`` keyword argument to the constructor of |
| | | :class:`~pyramid.config.Configurator` for defining a factory that will return |
| | | a custom ``Response`` class. See https://github.com/Pylons/pyramid/pull/1499 |
| | | a custom ``Response`` class. See https://github.com/Pylons/pyramid/pull/1499 |
| | | |
| | | - Add :attr:`pyramid.config.Configurator.root_package` attribute and init |
| | | parameter to assist with includeable packages that wish to resolve |
| | | resources relative to the package in which the configurator was created. |
| | | This is especially useful for addons that need to load asset specs from |
| | | settings, in which case it is may be natural for a developer to define |
| | | imports or assets relative to the top-level package. |
| | | See https://github.com/Pylons/pyramid/pull/1337 |
| | | - Added :attr:`pyramid.config.Configurator.root_package` attribute and init |
| | | parameter to assist with includible packages that wish to resolve resources |
| | | relative to the package in which the configurator was created. This is |
| | | especially useful for add-ons that need to load asset specs from settings, in |
| | | which case it may be natural for a developer to define imports or assets |
| | | relative to the top-level package. See |
| | | https://github.com/Pylons/pyramid/pull/1337 |
| | | |
| | | - Overall improvments for the ``proutes`` command. Added ``--format`` and |
| | | ``--glob`` arguments to the command, introduced the ``method`` |
| | | column for displaying available request methods, and improved the ``view`` |
| | | output by showing the module instead of just ``__repr__``. |
| | | See https://github.com/Pylons/pyramid/pull/1488 |
| | | output by showing the module instead of just ``__repr__``. See |
| | | https://github.com/Pylons/pyramid/pull/1488 |
| | | |
| | | - ``pserve`` can now take a ``-b`` or ``--browser`` option to open the server |
| | | URL in a web browser. See https://github.com/Pylons/pyramid/pull/1533 |
| | | |
| | | - Support keyword-only arguments and function annotations in views in |
| | | Python 3. See https://github.com/Pylons/pyramid/pull/1556 |
| | | - Support keyword-only arguments and function annotations in views in Python 3. |
| | | See https://github.com/Pylons/pyramid/pull/1556 |
| | | |
| | | - The ``append_slash`` argument of |
| | | :meth:`~pyramid.config.Configurator.add_notfound_view()` will now accept |
| | | anything that implements the :class:`~pyramid.interfaces.IResponse` interface |
| | | and will use that as the response class instead of the default |
| | | :class:`~pyramid.httpexceptions.HTTPFound`. See |
| | | :class:`~pyramid.httpexceptions.HTTPFound`. See |
| | | https://github.com/Pylons/pyramid/pull/1610 |
| | | |
| | | - The :class:`~pyramid.config.Configurator` has grown the ability to allow |
| | | actions to call other actions during a commit-cycle. This enables much more |
| | | actions to call other actions during a commit cycle. This enables much more |
| | | logic to be placed into actions, such as the ability to invoke other actions |
| | | or group them for improved conflict detection. We have also exposed and |
| | | documented the config phases that Pyramid uses in order to further assist in |
| | | building conforming addons. See https://github.com/Pylons/pyramid/pull/1513 |
| | | documented the configuration phases that Pyramid uses in order to further |
| | | assist in building conforming add-ons. See |
| | | https://github.com/Pylons/pyramid/pull/1513 |
| | | |
| | | - Allow an iterator to be returned from a renderer. Previously it was only |
| | | possible to return bytes or unicode. |
| | | See https://github.com/Pylons/pyramid/pull/1417 |
| | | possible to return bytes or unicode. See |
| | | https://github.com/Pylons/pyramid/pull/1417 |
| | | |
| | | - Improve robustness to timing attacks in the |
| | | :class:`~pyramid.authentication.AuthTktCookieHelper` and the |
| | | :class:`~pyramid.session.SignedCookieSessionFactory` classes by using the |
| | | stdlib's ``hmac.compare_digest`` if it is available (such as Python 2.7.7+ and |
| | | 3.3+). See https://github.com/Pylons/pyramid/pull/1457 |
| | | stdlib's ``hmac.compare_digest`` if it is available (such as Python 2.7.7+ |
| | | and 3.3+). See https://github.com/Pylons/pyramid/pull/1457 |
| | | |
| | | - Improve the readability of the ``pcreate`` shell script output. |
| | | See https://github.com/Pylons/pyramid/pull/1453 |
| | | - Improve the readability of the ``pcreate`` shell script output. See |
| | | https://github.com/Pylons/pyramid/pull/1453 |
| | | |
| | | - Make it simple to define notfound and forbidden views that wish to use the |
| | | default exception-response view but with altered predicates and other |
| | | configuration options. The ``view`` argument is now optional in |
| | | - Make it simple to define ``notfound`` and ``forbidden`` views that wish to |
| | | use the default exception-response view, but with altered predicates and |
| | | other configuration options. The ``view`` argument is now optional in |
| | | :meth:`~pyramid.config.Configurator.add_notfound_view` and |
| | | :meth:`~pyramid.config.Configurator.add_forbidden_view` See |
| | | https://github.com/Pylons/pyramid/issues/494 |
| | | |
| | | - The ``pshell`` script will now load a ``PYTHONSTARTUP`` file if one is |
| | | defined in the environment prior to launching the interpreter. |
| | | See https://github.com/Pylons/pyramid/pull/1448 |
| | | defined in the environment prior to launching the interpreter. See |
| | | https://github.com/Pylons/pyramid/pull/1448 |
| | | |
| | | - Add new HTTP exception objects for status codes |
| | | ``428 Precondition Required``, ``429 Too Many Requests`` and |
| | | ``431 Request Header Fields Too Large`` in ``pyramid.httpexceptions``. |
| | | See https://github.com/Pylons/pyramid/pull/1372/files |
| | | - Add new HTTP exception objects for status codes ``428 Precondition |
| | | Required``, ``429 Too Many Requests`` and ``431 Request Header Fields Too |
| | | Large`` in ``pyramid.httpexceptions``. See |
| | | https://github.com/Pylons/pyramid/pull/1372/files |
| | | |
| | | - ``pcreate`` when run without a scaffold argument will now print information |
| | | on the missing flag, as well as a list of available scaffolds. See |
| | | on the missing flag, as well as a list of available scaffolds. See |
| | | https://github.com/Pylons/pyramid/pull/1566 and |
| | | https://github.com/Pylons/pyramid/issues/1297 |
| | | |
| | | - ``pcreate`` will now ask for confirmation if invoked with an argument for a |
| | | project name that already exists or is importable in the current environment. |
| | | See https://github.com/Pylons/pyramid/issues/1357 and |
| | | https://github.com/Pylons/pyramid/pull/1837 |
| | | |
| | | - Add :func:`pyramid.request.apply_request_extensions` function which can be |
| | | used in testing to apply any request extensions configured via |
| | | ``config.add_request_method``. Previously it was only possible to test the |
| | | extensions by going through Pyramid's router. See |
| | | extensions by going through Pyramid's router. See |
| | | https://github.com/Pylons/pyramid/pull/1581 |
| | | |
| | | |
| | | - Make it possible to subclass ``pyramid.request.Request`` and also use |
| | | ``pyramid.request.Request.add_request.method``. See |
| | | ``pyramid.request.Request.add_request.method``. See |
| | | https://github.com/Pylons/pyramid/issues/1529 |
| | | |
| | | - Additional shells for ``pshell`` can now be registered as entry points. See |
| | | https://github.com/Pylons/pyramid/pull/1891 and |
| | | https://github.com/Pylons/pyramid/pull/2012 |
| | | |
| | | - The variables injected into ``pshell`` are now displayed with their |
| | | docstrings instead of the default ``str(obj)`` when possible. See |
| | | https://github.com/Pylons/pyramid/pull/1929 |
| | | |
| | | |
| | | Deprecations |
| | | ------------ |
| | | |
| | | - The ``pserve`` command's daemonization features, as well as |
| | | ``--monitor-restart``, have been deprecated. This includes the |
| | | ``[start,stop,restart,status]`` subcommands, as well as the ``--daemon``, |
| | | ``--stop-daemon``, ``--pid-file``, ``--status``, ``--user``, and ``--group`` |
| | | flags. See https://github.com/Pylons/pyramid/pull/2120 and |
| | | https://github.com/Pylons/pyramid/pull/2189 and |
| | | https://github.com/Pylons/pyramid/pull/1641 |
| | | |
| | | Please use a real process manager in the future instead of relying on |
| | | ``pserve`` to daemonize itself. Many options exist, including your operating |
| | | system's services, such as Systemd or Upstart, as well as Python-based |
| | | solutions like Circus and Supervisor. |
| | | |
| | | See https://github.com/Pylons/pyramid/pull/1641 and |
| | | https://github.com/Pylons/pyramid/pull/2120 |
| | | |
| | | - The ``principal`` argument to :func:`pyramid.security.remember` was renamed |
| | | to ``userid``. Using ``principal`` as the argument name still works and will |
| | | to ``userid``. Using ``principal`` as the argument name still works and will |
| | | continue to work for the next few releases, but a deprecation warning is |
| | | printed. |
| | | |
| | |
| | | - Added line numbers to the log formatters in the scaffolds to assist with |
| | | debugging. See https://github.com/Pylons/pyramid/pull/1326 |
| | | |
| | | - Update scaffold generating machinery to return the version of pyramid and |
| | | pyramid docs for use in scaffolds. Updated ``starter``, ``alchemy`` and |
| | | ``zodb`` templates to have links to correctly versioned documentation and |
| | | reflect which pyramid was used to generate the scaffold. |
| | | - Updated scaffold generating machinery to return the version of :app:`Pyramid` |
| | | and its documentation for use in scaffolds. Updated ``starter``, ``alchemy`` |
| | | and ``zodb`` templates to have links to correctly versioned documentation, |
| | | and to reflect which :app:`Pyramid` was used to generate the scaffold. |
| | | |
| | | - Removed non-ascii copyright symbol from templates, as this was |
| | | causing the scaffolds to fail for project generation. |
| | | - Removed non-ASCII copyright symbol from templates, as this was causing the |
| | | scaffolds to fail for project generation. |
| | | |
| | | |
| | | Documentation Enhancements |
| | | -------------------------- |
| | | |
| | | - Removed logging configuration from Quick Tutorial ini files except for |
| | | scaffolding- and logging-related chapters to avoid needing to explain it too |
| | | - Removed logging configuration from Quick Tutorial ``ini`` files, except for |
| | | scaffolding- and logging-related chapters, to avoid needing to explain it too |
| | | early. |
| | | |
| | | - Improve and clarify the documentation on what Pyramid defines as a |
| | | ``principal`` and a ``userid`` in its security APIs. |
| | | See https://github.com/Pylons/pyramid/pull/1399 |
| | | - Improve and clarify the documentation on what :app:`Pyramid` defines as a |
| | | ``principal`` and a ``userid`` in its security APIs. See |
| | | https://github.com/Pylons/pyramid/pull/1399 |
| | | |
| | | - Moved the documentation for ``accept`` on |
| | | :meth:`pyramid.config.Configurator.add_view` to no longer be part of the |
| | | predicate list. See https://github.com/Pylons/pyramid/issues/1391 for a bug |
| | | report stating ``not_`` was failing on ``accept``. Discussion with @mcdonc |
| | | led to the conclusion that it should not be documented as a predicate. |
| | | See https://github.com/Pylons/pyramid/pull/1487 for this PR. |
| | | |
| | | - Clarify a previously-implied detail of the ``ISession.invalidate`` API |
| | | documentation. |
| | | |
| | | - Add documentation of command line programs (``p*`` scripts). See |
| | | https://github.com/Pylons/pyramid/pull/2191 |
| | |
| | | self.cookie_name = cookie_name |
| | | self.secure = secure |
| | | self.include_ip = include_ip |
| | | self.timeout = timeout |
| | | self.reissue_time = reissue_time |
| | | self.max_age = max_age |
| | | self.timeout = timeout if timeout is None else int(timeout) |
| | | self.reissue_time = reissue_time if reissue_time is None else int(reissue_time) |
| | | self.max_age = max_age if max_age is None else int(max_age) |
| | | self.wild_domain = wild_domain |
| | | self.parent_domain = parent_domain |
| | | self.domain = domain |
| | |
| | | Tokens are available in the returned identity when an auth_tkt is |
| | | found in the request and unpacked. Default: ``()``. |
| | | """ |
| | | if max_age is None: |
| | | max_age = self.max_age |
| | | max_age = self.max_age if max_age is None else int(max_age) |
| | | |
| | | environ = request.environ |
| | | |
| | |
| | | def __init__(self, prefix): |
| | | self.prefix = prefix |
| | | |
| | | def get_filename(self, resource_name): |
| | | def get_path(self, resource_name): |
| | | if resource_name: |
| | | path = os.path.join(self.prefix, resource_name) |
| | | else: |
| | | path = self.prefix |
| | | return path |
| | | |
| | | def get_filename(self, resource_name): |
| | | path = self.get_path(resource_name) |
| | | if os.path.exists(path): |
| | | return path |
| | | |
| | |
| | | |
| | | Pass a key/value pair here to use a third-party predicate |
| | | registered via |
| | | :meth:`pyramid.config.Configurator.add_view_predicate`. More than |
| | | :meth:`pyramid.config.Configurator.add_route_predicate`. More than |
| | | one key/value pair can be used at the same time. See |
| | | :ref:`view_and_route_predicates` for more information about |
| | | third-party predicates. |
| | |
| | | |
| | | @action_method |
| | | def add_route_predicate(self, name, factory, weighs_more_than=None, |
| | | weighs_less_than=None): |
| | | weighs_less_than=None): |
| | | """ Adds a route predicate factory. The view predicate can later be |
| | | named as a keyword argument to |
| | | :meth:`pyramid.config.Configurator.add_route`. |
| | | |
| | | ``name`` should be the name of the predicate. It must be a valid |
| | | Python identifier (it will be used as a keyword argument to |
| | | ``add_view``). |
| | | ``add_route``). |
| | | |
| | | ``factory`` should be a :term:`predicate factory` or :term:`dotted |
| | | Python name` which refers to a predicate factory. |
| | |
| | | import inspect |
| | | import posixpath |
| | | import operator |
| | | import os |
| | | import warnings |
| | |
| | | IException, |
| | | IExceptionViewClassifier, |
| | | IMultiView, |
| | | IPackageOverrides, |
| | | IRendererFactory, |
| | | IRequest, |
| | | IResponse, |
| | |
| | | ) |
| | | |
| | | from pyramid import renderers |
| | | from pyramid.static import PathSegmentMd5CacheBuster |
| | | |
| | | from pyramid.asset import resolve_asset_spec |
| | | from pyramid.compat import ( |
| | | string_types, |
| | | urlparse, |
| | |
| | | |
| | | from pyramid.security import NO_PERMISSION_REQUIRED |
| | | from pyramid.static import static_view |
| | | from pyramid.threadlocal import get_current_registry |
| | | |
| | | from pyramid.url import parse_url_overrides |
| | | |
| | |
| | | signatures than the ones supported by :app:`Pyramid` as described in |
| | | its narrative documentation. |
| | | |
| | | The ``mapper`` should argument be an object implementing |
| | | The ``mapper`` argument should be an object implementing |
| | | :class:`pyramid.interfaces.IViewMapperFactory` or a :term:`dotted |
| | | Python name` to such an object. The provided ``mapper`` will become |
| | | the default view mapper to be used by all subsequent :term:`view |
| | |
| | | ``Expires`` and ``Cache-Control`` headers for static assets served. |
| | | Note that this argument has no effect when the ``name`` is a *url |
| | | prefix*. By default, this argument is ``None``, meaning that no |
| | | particular Expires or Cache-Control headers are set in the response, |
| | | unless ``cachebust`` is specified. |
| | | |
| | | The ``cachebust`` keyword argument may be set to cause |
| | | :meth:`~pyramid.request.Request.static_url` to use cache busting when |
| | | generating URLs. See :ref:`cache_busting` for general information |
| | | about cache busting. The value of the ``cachebust`` argument may be |
| | | ``True``, in which case a default cache busting implementation is used. |
| | | The value of the ``cachebust`` argument may also be an object which |
| | | implements :class:`~pyramid.interfaces.ICacheBuster`. See the |
| | | :mod:`~pyramid.static` module for some implementations. If the |
| | | ``cachebust`` argument is provided, the default for ``cache_max_age`` |
| | | is modified to be ten years. ``cache_max_age`` may still be explicitly |
| | | provided to override this default. |
| | | particular Expires or Cache-Control headers are set in the response. |
| | | |
| | | The ``permission`` keyword argument is used to specify the |
| | | :term:`permission` required by a user to execute the static view. By |
| | |
| | | See :ref:`static_assets_section` for more information. |
| | | """ |
| | | spec = self._make_spec(path) |
| | | info = self._get_static_info() |
| | | info.add(self, name, spec, **kw) |
| | | |
| | | def add_cache_buster(self, path, cachebust, explicit=False): |
| | | """ |
| | | Add a cache buster to a set of files on disk. |
| | | |
| | | The ``path`` should be the path on disk where the static files |
| | | reside. This can be an absolute path, a package-relative path, or a |
| | | :term:`asset specification`. |
| | | |
| | | The ``cachebust`` argument may be set to cause |
| | | :meth:`~pyramid.request.Request.static_url` to use cache busting when |
| | | generating URLs. See :ref:`cache_busting` for general information |
| | | about cache busting. The value of the ``cachebust`` argument must |
| | | be an object which implements |
| | | :class:`~pyramid.interfaces.ICacheBuster`. |
| | | |
| | | If ``explicit`` is set to ``True`` then the ``path`` for the cache |
| | | buster will be matched based on the ``rawspec`` instead of the |
| | | ``pathspec`` as defined in the |
| | | :class:`~pyramid.interfaces.ICacheBuster` interface. |
| | | Default: ``False``. |
| | | |
| | | """ |
| | | spec = self._make_spec(path) |
| | | info = self._get_static_info() |
| | | info.add_cache_buster(self, spec, cachebust, explicit=explicit) |
| | | |
| | | def _get_static_info(self): |
| | | info = self.registry.queryUtility(IStaticURLInfo) |
| | | if info is None: |
| | | info = StaticURLInfo() |
| | | self.registry.registerUtility(info, IStaticURLInfo) |
| | | info.add(self, name, spec, **kw) |
| | | return info |
| | | |
| | | def isexception(o): |
| | | if IInterface.providedBy(o): |
| | |
| | | |
| | | @implementer(IStaticURLInfo) |
| | | class StaticURLInfo(object): |
| | | # Indirection for testing |
| | | _default_cachebust = PathSegmentMd5CacheBuster |
| | | |
| | | def _get_registrations(self, registry): |
| | | try: |
| | | reg = registry._static_url_registrations |
| | | except AttributeError: |
| | | reg = registry._static_url_registrations = [] |
| | | return reg |
| | | def __init__(self): |
| | | self.registrations = [] |
| | | self.cache_busters = [] |
| | | |
| | | def generate(self, path, request, **kw): |
| | | try: |
| | | registry = request.registry |
| | | except AttributeError: # bw compat (for tests) |
| | | registry = get_current_registry() |
| | | registrations = self._get_registrations(registry) |
| | | for (url, spec, route_name, cachebust) in registrations: |
| | | for (url, spec, route_name) in self.registrations: |
| | | if path.startswith(spec): |
| | | subpath = path[len(spec):] |
| | | if WIN: # pragma: no cover |
| | | subpath = subpath.replace('\\', '/') # windows |
| | | if cachebust: |
| | | subpath, kw = cachebust(subpath, kw) |
| | | if self.cache_busters: |
| | | subpath, kw = self._bust_asset_path( |
| | | request, spec, subpath, kw) |
| | | if url is None: |
| | | kw['subpath'] = subpath |
| | | return request.route_url(route_name, **kw) |
| | |
| | | # make sure it ends with a slash |
| | | name = name + '/' |
| | | |
| | | if config.registry.settings.get('pyramid.prevent_cachebust'): |
| | | cb = None |
| | | else: |
| | | cb = extra.pop('cachebust', None) |
| | | if cb is True: |
| | | cb = self._default_cachebust() |
| | | if cb: |
| | | def cachebust(subpath, kw): |
| | | subpath_tuple = tuple(subpath.split('/')) |
| | | subpath_tuple, kw = cb.pregenerate( |
| | | spec + subpath, subpath_tuple, kw) |
| | | return '/'.join(subpath_tuple), kw |
| | | else: |
| | | cachebust = None |
| | | |
| | | if url_parse(name).netloc: |
| | | # it's a URL |
| | | # url, spec, route_name |
| | |
| | | else: |
| | | # it's a view name |
| | | url = None |
| | | ten_years = 10 * 365 * 24 * 60 * 60 # more or less |
| | | default = ten_years if cb else None |
| | | cache_max_age = extra.pop('cache_max_age', default) |
| | | cache_max_age = extra.pop('cache_max_age', None) |
| | | |
| | | # create a view |
| | | cb_match = getattr(cb, 'match', None) |
| | | view = static_view(spec, cache_max_age=cache_max_age, |
| | | use_subpath=True, cachebust_match=cb_match) |
| | | use_subpath=True) |
| | | |
| | | # Mutate extra to allow factory, etc to be passed through here. |
| | | # Treat permission specially because we'd like to default to |
| | |
| | | permission=permission, |
| | | context=context, |
| | | renderer=renderer, |
| | | ) |
| | | ) |
| | | |
| | | def register(): |
| | | registrations = self._get_registrations(config.registry) |
| | | registrations = self.registrations |
| | | |
| | | names = [ t[0] for t in registrations ] |
| | | names = [t[0] for t in registrations] |
| | | |
| | | if name in names: |
| | | idx = names.index(name) |
| | | registrations.pop(idx) |
| | | |
| | | # url, spec, route_name |
| | | registrations.append((url, spec, route_name, cachebust)) |
| | | registrations.append((url, spec, route_name)) |
| | | |
| | | intr = config.introspectable('static views', |
| | | name, |
| | |
| | | |
| | | config.action(None, callable=register, introspectables=(intr,)) |
| | | |
| | | def add_cache_buster(self, config, spec, cachebust, explicit=False): |
| | | # ensure the spec always has a trailing slash as we only support |
| | | # adding cache busters to folders, not files |
| | | if os.path.isabs(spec): # FBO windows |
| | | sep = os.sep |
| | | else: |
| | | sep = '/' |
| | | if not spec.endswith(sep) and not spec.endswith(':'): |
| | | spec = spec + sep |
| | | |
| | | def register(): |
| | | if config.registry.settings.get('pyramid.prevent_cachebust'): |
| | | return |
| | | |
| | | cache_busters = self.cache_busters |
| | | |
| | | # find duplicate cache buster (old_idx) |
| | | # and insertion location (new_idx) |
| | | new_idx, old_idx = len(cache_busters), None |
| | | for idx, (spec_, cb_, explicit_) in enumerate(cache_busters): |
| | | # if we find an identical (spec, explicit) then use it |
| | | if spec == spec_ and explicit == explicit_: |
| | | old_idx = new_idx = idx |
| | | break |
| | | |
| | | # past all explicit==False specs then add to the end |
| | | elif not explicit and explicit_: |
| | | new_idx = idx |
| | | break |
| | | |
| | | # explicit matches and spec is shorter |
| | | elif explicit == explicit_ and len(spec) < len(spec_): |
| | | new_idx = idx |
| | | break |
| | | |
| | | if old_idx is not None: |
| | | cache_busters.pop(old_idx) |
| | | |
| | | cache_busters.insert(new_idx, (spec, cachebust, explicit)) |
| | | |
| | | intr = config.introspectable('cache busters', |
| | | spec, |
| | | 'cache buster for %r' % spec, |
| | | 'cache buster') |
| | | intr['cachebust'] = cachebust |
| | | intr['path'] = spec |
| | | intr['explicit'] = explicit |
| | | |
| | | config.action(None, callable=register, introspectables=(intr,)) |
| | | |
| | | def _bust_asset_path(self, request, spec, subpath, kw): |
| | | registry = request.registry |
| | | pkg_name, pkg_subpath = resolve_asset_spec(spec) |
| | | rawspec = None |
| | | |
| | | if pkg_name is not None: |
| | | pathspec = '{0}:{1}{2}'.format(pkg_name, pkg_subpath, subpath) |
| | | overrides = registry.queryUtility(IPackageOverrides, name=pkg_name) |
| | | if overrides is not None: |
| | | resource_name = posixpath.join(pkg_subpath, subpath) |
| | | sources = overrides.filtered_sources(resource_name) |
| | | for source, filtered_path in sources: |
| | | rawspec = source.get_path(filtered_path) |
| | | if hasattr(source, 'pkg_name'): |
| | | rawspec = '{0}:{1}'.format(source.pkg_name, rawspec) |
| | | break |
| | | |
| | | else: |
| | | pathspec = pkg_subpath + subpath |
| | | |
| | | if rawspec is None: |
| | | rawspec = pathspec |
| | | |
| | | kw['pathspec'] = pathspec |
| | | kw['rawspec'] = rawspec |
| | | for spec_, cachebust, explicit in reversed(self.cache_busters): |
| | | if ( |
| | | (explicit and rawspec.startswith(spec_)) or |
| | | (not explicit and pathspec.startswith(spec_)) |
| | | ): |
| | | subpath, kw = cachebust(request, subpath, kw) |
| | | break |
| | | return subpath, kw |
| | |
| | | def generate(path, request, **kw): |
| | | """ Generate a URL for the given path """ |
| | | |
| | | def add_cache_buster(config, spec, cache_buster): |
| | | """ Add a new cache buster to a particular set of assets """ |
| | | |
| | | class IResponseFactory(Interface): |
| | | """ A utility which generates a response """ |
| | | def __call__(request): |
| | |
| | | |
| | | class ICacheBuster(Interface): |
| | | """ |
| | | Instances of ``ICacheBuster`` may be provided as arguments to |
| | | :meth:`~pyramid.config.Configurator.add_static_view`. Instances of |
| | | ``ICacheBuster`` provide mechanisms for generating a cache bust token for |
| | | a static asset, modifying a static asset URL to include a cache bust token, |
| | | and, optionally, unmodifying a static asset URL in order to look up an |
| | | asset. See :ref:`cache_busting`. |
| | | A cache buster modifies the URL generation machinery for |
| | | :meth:`~pyramid.request.Request.static_url`. See :ref:`cache_busting`. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def pregenerate(pathspec, subpath, kw): |
| | | def __call__(request, subpath, kw): |
| | | """ |
| | | Modifies a subpath and/or keyword arguments from which a static asset |
| | | URL will be computed during URL generation. The ``pathspec`` argument |
| | | is the path specification for the resource to be cache busted. |
| | | The ``subpath`` argument is a tuple of path elements that represent the |
| | | portion of the asset URL which is used to find the asset. The ``kw`` |
| | | argument is a dict of keywords that are to be passed eventually to |
| | | :meth:`~pyramid.request.Request.route_url` for URL generation. The |
| | | return value should be a two-tuple of ``(subpath, kw)`` which are |
| | | versions of the same arguments modified to include the cache bust token |
| | | in the generated URL. |
| | | """ |
| | | URL will be computed during URL generation. |
| | | |
| | | def match(subpath): |
| | | """ |
| | | Performs the logical inverse of |
| | | :meth:`~pyramid.interfaces.ICacheBuster.pregenerate` by taking a |
| | | subpath from a cache busted URL and removing the cache bust token, so |
| | | that :app:`Pyramid` can find the underlying asset. |
| | | The ``subpath`` argument is a path of ``/``-delimited segments that |
| | | represent the portion of the asset URL which is used to find the asset. |
| | | The ``kw`` argument is a dict of keywords that are to be passed |
| | | eventually to :meth:`~pyramid.request.Request.static_url` for URL |
| | | generation. The return value should be a two-tuple of |
| | | ``(subpath, kw)`` where ``subpath`` is the relative URL from where the |
| | | file is served and ``kw`` is the same input argument. The return value |
| | | should be modified to include the cache bust token in the generated |
| | | URL. |
| | | |
| | | ``subpath`` is the subpath portion of the URL for an incoming request |
| | | for a static asset. The return value should be the same tuple with the |
| | | cache busting token elided. |
| | | The ``kw`` dictionary contains extra arguments passed to |
| | | :meth:`~pyramid.request.Request.static_url` as well as some extra |
| | | items that may be usful including: |
| | | |
| | | If the cache busting scheme in use doesn't specifically modify the path |
| | | portion of the generated URL (e.g. it adds a query string), a method |
| | | which implements this interface may not be necessary. It is |
| | | permissible for an instance of |
| | | :class:`~pyramid.interfaces.ICacheBuster` to omit this method. |
| | | - ``pathspec`` is the path specification for the resource |
| | | to be cache busted. |
| | | |
| | | - ``rawspec`` is the original location of the file, ignoring |
| | | any calls to :meth:`pyramid.config.Configurator.override_asset`. |
| | | |
| | | The ``pathspec`` and ``rawspec`` values are only different in cases |
| | | where an asset has been mounted into a virtual location using |
| | | :meth:`pyramid.config.Configurator.override_asset`. For example, with |
| | | a call to ``request.static_url('myapp:static/foo.png'), the |
| | | ``pathspec`` is ``myapp:static/foo.png`` whereas the ``rawspec`` may |
| | | be ``themepkg:bar.png``, assuming a call to |
| | | ``config.override_asset('myapp:static/foo.png', 'themepkg:bar.png')``. |
| | | """ |
| | | |
| | | # configuration phases: a lower phase number means the actions associated |
| | |
| | | return '%s:%s' % (self.pkg_name, self.path) |
| | | |
| | | def abspath(self): |
| | | return self.pkg_resources.resource_filename(self.pkg_name, self.path) |
| | | return os.path.abspath( |
| | | self.pkg_resources.resource_filename(self.pkg_name, self.path)) |
| | | |
| | | def stream(self): |
| | | return self.pkg_resources.resource_stream(self.pkg_name, self.path) |
| | |
| | | elif use_pkg_resources: |
| | | content = pkg_resources.resource_string(source[0], full) |
| | | else: |
| | | f = open(full, 'rb') |
| | | content = f.read() |
| | | f.close() |
| | | with open(full, 'rb') as f: |
| | | content = f.read() |
| | | if sub_file: |
| | | try: |
| | | content = substitute_content( |
| | |
| | | continue # pragma: no cover |
| | | already_exists = os.path.exists(dest_full) |
| | | if already_exists: |
| | | f = open(dest_full, 'rb') |
| | | old_content = f.read() |
| | | f.close() |
| | | with open(dest_full, 'rb') as f: |
| | | old_content = f.read() |
| | | if old_content == content: |
| | | if verbosity: |
| | | out('%s%s already exists (same content)' % |
| | |
| | | '%sCopying %s to %s' % (pad, os.path.basename(full), |
| | | dest_full)) |
| | | if not simulate: |
| | | f = open(dest_full, 'wb') |
| | | f.write(content) |
| | | f.close() |
| | | with open(dest_full, 'wb') as f: |
| | | f.write(content) |
| | | |
| | | def should_skip_file(name): |
| | | """ |
| | |
| | | |
| | | class PRequestCommand(object): |
| | | description = """\ |
| | | Run a request for the described application. |
| | | Submit a HTTP request to a web application. |
| | | |
| | | This command makes an artifical request to a web application that uses a |
| | | PasteDeploy (.ini) configuration file for the server and application. |
| | |
| | | import logging |
| | | import optparse |
| | | import os |
| | | import py_compile |
| | | import re |
| | | import subprocess |
| | | import sys |
| | | import tempfile |
| | | import textwrap |
| | | import threading |
| | | import time |
| | |
| | | |
| | | If start/stop/restart is given, then --daemon is implied, and it will |
| | | start (normal operation), stop (--stop-daemon), or do both. |
| | | Note: Daemonization features are deprecated. |
| | | |
| | | You can also include variable assignments like 'http_port=8080' |
| | | and then use %(http_port)s in your config files. |
| | |
| | | '--daemon', |
| | | dest="daemon", |
| | | action="store_true", |
| | | help="Run in daemon (background) mode") |
| | | help="Run in daemon (background) mode [DEPRECATED]") |
| | | parser.add_option( |
| | | '--pid-file', |
| | | dest='pid_file', |
| | | metavar='FILENAME', |
| | | help=("Save PID to file (default to pyramid.pid if running in " |
| | | "daemon mode)")) |
| | | "daemon mode) [DEPRECATED]")) |
| | | parser.add_option( |
| | | '--log-file', |
| | | dest='log_file', |
| | |
| | | '--monitor-restart', |
| | | dest='monitor_restart', |
| | | action='store_true', |
| | | help="Auto-restart server if it dies") |
| | | help="Auto-restart server if it dies [DEPRECATED]") |
| | | parser.add_option( |
| | | '-b', '--browser', |
| | | dest='browser', |
| | |
| | | '--status', |
| | | action='store_true', |
| | | dest='show_status', |
| | | help="Show the status of the (presumably daemonized) server") |
| | | help=("Show the status of the (presumably daemonized) server " |
| | | "[DEPRECATED]")) |
| | | parser.add_option( |
| | | '-v', '--verbose', |
| | | default=default_verbosity, |
| | |
| | | dest='stop_daemon', |
| | | action='store_true', |
| | | help=('Stop a daemonized server (given a PID file, or default ' |
| | | 'pyramid.pid file)')) |
| | | 'pyramid.pid file) [DEPRECATED]')) |
| | | |
| | | _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I) |
| | | |
| | |
| | | self.options.set_user = self.options.set_group = None |
| | | |
| | | # @@: Is this the right stage to set the user at? |
| | | self.change_user_group( |
| | | self.options.set_user, self.options.set_group) |
| | | if self.options.set_user or self.options.set_group: |
| | | self.change_user_group( |
| | | self.options.set_user, self.options.set_group) |
| | | |
| | | if not self.args: |
| | | self.out('You must give a config file') |
| | |
| | | cmd = None |
| | | |
| | | if self.options.reload: |
| | | if ( |
| | | getattr(self.options, 'daemon', False) or |
| | | cmd in ('start', 'stop', 'restart') |
| | | ): |
| | | self.out( |
| | | 'Error: Cannot use reloading while running as a dameon.') |
| | | return 2 |
| | | if os.environ.get(self._reloader_environ_key): |
| | | if self.options.verbose > 1: |
| | | self.out('Running reloading file monitor') |
| | |
| | | self._warn_daemon_deprecated() |
| | | return self.show_status() |
| | | |
| | | if cmd == 'restart' or cmd == 'stop': |
| | | if cmd in ('restart', 'stop'): |
| | | self._warn_daemon_deprecated() |
| | | result = self.stop_daemon() |
| | | if result: |
| | |
| | | raise ValueError(msg) |
| | | writeable_pid_file.close() |
| | | |
| | | if getattr(self.options, 'daemon', False): |
| | | # warn before forking |
| | | if ( |
| | | self.options.monitor_restart and |
| | | not os.environ.get(self._monitor_environ_key) |
| | | ): |
| | | self.out('''\ |
| | | --monitor-restart has been deprecated in Pyramid 1.6. It will be removed |
| | | in a future release per Pyramid's deprecation policy. Please consider using |
| | | a real process manager for your processes like Systemd, Circus, or Supervisor. |
| | | ''') |
| | | |
| | | if ( |
| | | getattr(self.options, 'daemon', False) and |
| | | not os.environ.get(self._monitor_environ_key) |
| | | ): |
| | | self._warn_daemon_deprecated() |
| | | try: |
| | | self.daemonize() |
| | |
| | | return 2 |
| | | |
| | | if ( |
| | | not os.environ.get(self._monitor_environ_key) and |
| | | self.options.pid_file |
| | | ): |
| | | self.record_pid(self.options.pid_file) |
| | | |
| | | if ( |
| | | self.options.monitor_restart and |
| | | not os.environ.get(self._monitor_environ_key) |
| | | ): |
| | | return self.restart_with_monitor() |
| | | |
| | | if self.options.pid_file: |
| | | self.record_pid(self.options.pid_file) |
| | | |
| | | if self.options.log_file: |
| | | stdout_log = LazyWriter(self.options.log_file, 'a') |
| | |
| | | "installed" % arg) |
| | | arg = win32api.GetShortPathName(arg) |
| | | return arg |
| | | |
| | | def find_script_path(self, name): # pragma: no cover |
| | | """ |
| | | Return the path to the script being invoked by the python interpreter. |
| | | |
| | | There's an issue on Windows when running the executable from |
| | | a console_script causing the script name (sys.argv[0]) to |
| | | not end with .exe or .py and thus cannot be run via popen. |
| | | """ |
| | | if sys.platform == 'win32': |
| | | if not name.endswith('.exe') and not name.endswith('.py'): |
| | | name += '.exe' |
| | | return name |
| | | |
| | | def daemonize(self): # pragma: no cover |
| | | pid = live_pidfile(self.options.pid_file) |
| | |
| | | else: |
| | | self.out('Starting subprocess with monitor parent') |
| | | while 1: |
| | | args = [self.quote_first_command_arg(sys.executable)] + sys.argv |
| | | args = [ |
| | | self.quote_first_command_arg(sys.executable), |
| | | self.find_script_path(sys.argv[0]), |
| | | ] + sys.argv[1:] |
| | | new_environ = os.environ.copy() |
| | | if reloader: |
| | | new_environ[self._reloader_environ_key] = 'true' |
| | |
| | | self.out('%s %s %s' % ('-' * 20, 'Restarting', '-' * 20)) |
| | | |
| | | def change_user_group(self, user, group): # pragma: no cover |
| | | if not user and not group: |
| | | return |
| | | import pwd |
| | | import grp |
| | | |
| | | self.out('''\ |
| | | The --user and --group options have been deprecated in Pyramid 1.6. They will |
| | | be removed in a future release per Pyramid's deprecation policy. Please |
| | | consider using a real process manager for your processes like Systemd, Circus, |
| | | or Supervisor, all of which support process security. |
| | | ''') |
| | | |
| | | uid = gid = None |
| | | if group: |
| | |
| | | if %errorlevel% == 3 goto repeat |
| | | |
| | | or run a monitoring process in Python (``pserve --reload`` does |
| | | this). |
| | | this). |
| | | |
| | | Use the ``watch_file(filename)`` function to cause a reload/restart for |
| | | other non-Python files (e.g., configuration files). If you have |
| | |
| | | self.poll_interval = poll_interval |
| | | self.extra_files = list(self.global_extra_files) |
| | | self.instances.append(self) |
| | | self.syntax_error_files = set() |
| | | self.pending_reload = False |
| | | self.file_callbacks = list(self.global_file_callbacks) |
| | | temp_pyc_fp = tempfile.NamedTemporaryFile(delete=False) |
| | | self.temp_pyc = temp_pyc_fp.name |
| | | temp_pyc_fp.close() |
| | | |
| | | def _exit(self): |
| | | try: |
| | | os.unlink(self.temp_pyc) |
| | | except IOError: |
| | | # not worried if the tempfile can't be removed |
| | | pass |
| | | # use os._exit() here and not sys.exit() since within a |
| | | # thread sys.exit() just closes the given thread and |
| | | # won't kill the process; note os._exit does not call |
| | |
| | | continue |
| | | if filename is not None: |
| | | filenames.append(filename) |
| | | new_changes = False |
| | | for filename in filenames: |
| | | try: |
| | | stat = os.stat(filename) |
| | |
| | | continue |
| | | if filename.endswith('.pyc') and os.path.exists(filename[:-1]): |
| | | mtime = max(os.stat(filename[:-1]).st_mtime, mtime) |
| | | if filename not in self.module_mtimes: |
| | | self.module_mtimes[filename] = mtime |
| | | elif self.module_mtimes[filename] < mtime: |
| | | print("%s changed; reloading..." % filename) |
| | | return False |
| | | pyc = True |
| | | else: |
| | | pyc = False |
| | | old_mtime = self.module_mtimes.get(filename) |
| | | self.module_mtimes[filename] = mtime |
| | | if old_mtime is not None and old_mtime < mtime: |
| | | new_changes = True |
| | | if pyc: |
| | | filename = filename[:-1] |
| | | is_valid = True |
| | | if filename.endswith('.py'): |
| | | is_valid = self.check_syntax(filename) |
| | | if is_valid: |
| | | print("%s changed ..." % filename) |
| | | if new_changes: |
| | | self.pending_reload = True |
| | | if self.syntax_error_files: |
| | | for filename in sorted(self.syntax_error_files): |
| | | print("%s has a SyntaxError; NOT reloading." % filename) |
| | | if self.pending_reload and not self.syntax_error_files: |
| | | self.pending_reload = False |
| | | return False |
| | | return True |
| | | |
| | | def check_syntax(self, filename): |
| | | # check if a file has syntax errors. |
| | | # If so, track it until it's fixed. |
| | | try: |
| | | py_compile.compile(filename, cfile=self.temp_pyc, doraise=True) |
| | | except py_compile.PyCompileError as ex: |
| | | print(ex.msg) |
| | | self.syntax_error_files.add(filename) |
| | | return False |
| | | else: |
| | | if filename in self.syntax_error_files: |
| | | self.syntax_error_files.remove(filename) |
| | | return True |
| | | |
| | | def watch_file(self, cls, filename): |
| | |
| | | |
| | | from pyramid.paster import setup_logging |
| | | |
| | | from pyramid.settings import aslist |
| | | |
| | | from pyramid.scripts.common import parse_vars |
| | | |
| | | def main(argv=sys.argv, quiet=False): |
| | | command = PShellCommand(argv, quiet) |
| | | return command.run() |
| | | |
| | | |
| | | def python_shell_runner(env, help, interact=interact): |
| | | cprt = 'Type "help" for more information.' |
| | | banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt) |
| | | banner += '\n\n' + help + '\n' |
| | | interact(banner, local=env) |
| | | |
| | | |
| | | class PShellCommand(object): |
| | |
| | | ) |
| | | parser.add_option('-p', '--python-shell', |
| | | action='store', type='string', dest='python_shell', |
| | | default='', help='ipython | bpython | python') |
| | | default='', |
| | | help=('Select the shell to use. A list of possible ' |
| | | 'shells is available using the --list-shells ' |
| | | 'option.')) |
| | | parser.add_option('-l', '--list-shells', |
| | | dest='list', |
| | | action='store_true', |
| | | help='List all available shells.') |
| | | parser.add_option('--setup', |
| | | dest='setup', |
| | | help=("A callable that will be passed the environment " |
| | |
| | | "[pshell] ini section.")) |
| | | |
| | | ConfigParser = configparser.ConfigParser # testing |
| | | default_runner = python_shell_runner # testing |
| | | |
| | | loaded_objects = {} |
| | | object_help = {} |
| | | preferred_shells = [] |
| | | setup = None |
| | | pystartup = os.environ.get('PYTHONSTARTUP') |
| | | |
| | |
| | | |
| | | def pshell_file_config(self, filename): |
| | | config = self.ConfigParser() |
| | | config.optionxform = str |
| | | config.read(filename) |
| | | try: |
| | | items = config.items('pshell') |
| | |
| | | for k, v in items: |
| | | if k == 'setup': |
| | | self.setup = v |
| | | elif k == 'default_shell': |
| | | self.preferred_shells = [x.lower() for x in aslist(v)] |
| | | else: |
| | | self.loaded_objects[k] = resolver.maybe_resolve(v) |
| | | self.object_help[k] = v |
| | |
| | | print(msg) |
| | | |
| | | def run(self, shell=None): |
| | | if self.options.list: |
| | | return self.show_shells() |
| | | if not self.args: |
| | | self.out('Requires a config file argument') |
| | | return 2 |
| | |
| | | finally: |
| | | self.closer() |
| | | |
| | | def make_shell(self): |
| | | shells = {} |
| | | def show_shells(self): |
| | | shells = self.find_all_shells() |
| | | sorted_names = sorted(shells.keys(), key=lambda x: x.lower()) |
| | | |
| | | for ep in self.pkg_resources.iter_entry_points('pyramid.pshell'): |
| | | self.out('Available shells:') |
| | | for name in sorted_names: |
| | | self.out(' %s' % (name,)) |
| | | return 0 |
| | | |
| | | def find_all_shells(self): |
| | | pkg_resources = self.pkg_resources |
| | | |
| | | shells = {} |
| | | for ep in pkg_resources.iter_entry_points('pyramid.pshell_runner'): |
| | | name = ep.name |
| | | shell_module = ep.load() |
| | | shells[name] = shell_module |
| | | shell_factory = ep.load() |
| | | shells[name] = shell_factory |
| | | return shells |
| | | |
| | | def make_shell(self): |
| | | shells = self.find_all_shells() |
| | | |
| | | shell = None |
| | | user_shell = self.options.python_shell.lower() |
| | | |
| | | if not user_shell: |
| | | sorted_shells = sorted(shells.items(), key=lambda x: x[0]) |
| | | for name, factory in sorted_shells: |
| | | shell = factory() |
| | | preferred_shells = self.preferred_shells |
| | | if not preferred_shells: |
| | | # by default prioritize all shells above python |
| | | preferred_shells = [k for k in shells.keys() if k != 'python'] |
| | | max_weight = len(preferred_shells) |
| | | def order(x): |
| | | # invert weight to reverse sort the list |
| | | # (closer to the front is higher priority) |
| | | try: |
| | | return preferred_shells.index(x[0].lower()) - max_weight |
| | | except ValueError: |
| | | return 1 |
| | | sorted_shells = sorted(shells.items(), key=order) |
| | | |
| | | if shell is not None: |
| | | break |
| | | if len(sorted_shells) > 0: |
| | | shell = sorted_shells[0][1] |
| | | |
| | | else: |
| | | factory = shells.get(user_shell) |
| | | runner = shells.get(user_shell) |
| | | |
| | | if factory is not None: |
| | | shell = factory() |
| | | else: |
| | | if runner is not None: |
| | | shell = runner |
| | | |
| | | if shell is None: |
| | | raise ValueError( |
| | | 'could not find a shell named "%s"' % user_shell |
| | | ) |
| | | |
| | | if shell is None: |
| | | shell = self.make_default_shell() |
| | | # should never happen, but just incase entry points are borked |
| | | shell = self.default_runner |
| | | |
| | | return shell |
| | | |
| | | def make_default_shell(self, interact=interact): |
| | | def shell(env, help): |
| | | cprt = 'Type "help" for more information.' |
| | | banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt) |
| | | banner += '\n\n' + help + '\n' |
| | | interact(banner, local=env) |
| | | return shell |
| | | |
| | | @classmethod |
| | | def make_bpython_shell(cls, BPShell=None): |
| | | if BPShell is None: # pragma: no cover |
| | | try: |
| | | from bpython import embed |
| | | BPShell = embed |
| | | except ImportError: |
| | | return None |
| | | def shell(env, help): |
| | | BPShell(locals_=env, banner=help + '\n') |
| | | return shell |
| | | |
| | | @classmethod |
| | | def make_ipython_shell(cls, IPShellFactory=None): |
| | | if IPShellFactory is None: # pragma: no cover |
| | | try: |
| | | from IPython.terminal.embed import ( |
| | | InteractiveShellEmbed) |
| | | IPShellFactory = InteractiveShellEmbed |
| | | except ImportError: |
| | | return None |
| | | def shell(env, help): |
| | | IPShell = IPShellFactory(banner2=help + '\n', user_ns=env) |
| | | IPShell() |
| | | return shell |
| | | |
| | | |
| | |
| | | return True |
| | | |
| | | class PickleSerializer(object): |
| | | """ A Webob cookie serializer that uses the pickle protocol to dump Python |
| | | data to bytes.""" |
| | | """ A serializer that uses the pickle protocol to dump Python |
| | | data to bytes. |
| | | |
| | | This is the default serializer used by Pyramid. |
| | | |
| | | ``protocol`` may be specified to control the version of pickle used. |
| | | Defaults to :attr:`pickle.HIGHEST_PROTOCOL`. |
| | | |
| | | """ |
| | | def __init__(self, protocol=pickle.HIGHEST_PROTOCOL): |
| | | self.protocol = protocol |
| | | |
| | | def loads(self, bstruct): |
| | | """Accept bytes and return a Python object.""" |
| | | return pickle.loads(bstruct) |
| | | |
| | | def dumps(self, appstruct): |
| | | return pickle.dumps(appstruct, pickle.HIGHEST_PROTOCOL) |
| | | """Accept a Python object and return bytes.""" |
| | | return pickle.dumps(appstruct, self.protocol) |
| | | |
| | | def BaseCookieSessionFactory( |
| | | serializer, |
| | |
| | | |
| | | # configuration parameters |
| | | _cookie_name = cookie_name |
| | | _cookie_max_age = max_age |
| | | _cookie_max_age = max_age if max_age is None else int(max_age) |
| | | _cookie_path = path |
| | | _cookie_domain = domain |
| | | _cookie_secure = secure |
| | | _cookie_httponly = httponly |
| | | _cookie_on_exception = set_on_exception |
| | | _timeout = timeout |
| | | _reissue_time = reissue_time |
| | | _timeout = timeout if timeout is None else int(timeout) |
| | | _reissue_time = reissue_time if reissue_time is None else int(reissue_time) |
| | | |
| | | # dirty flag |
| | | _dirty = False |
| | |
| | | # -*- coding: utf-8 -*- |
| | | import hashlib |
| | | import json |
| | | import os |
| | | |
| | | from os.path import ( |
| | | getmtime, |
| | | normcase, |
| | | normpath, |
| | | join, |
| | |
| | | |
| | | from repoze.lru import lru_cache |
| | | |
| | | from pyramid.asset import resolve_asset_spec |
| | | from pyramid.asset import ( |
| | | abspath_from_asset_spec, |
| | | resolve_asset_spec, |
| | | ) |
| | | |
| | | from pyramid.compat import text_ |
| | | |
| | |
| | | HTTPMovedPermanently, |
| | | ) |
| | | |
| | | from pyramid.path import AssetResolver, caller_package |
| | | from pyramid.path import caller_package |
| | | from pyramid.response import FileResponse |
| | | from pyramid.traversal import traversal_path_info |
| | | |
| | |
| | | encoded = slash.join(path_tuple) # will be unicode |
| | | return encoded |
| | | |
| | | def _generate_md5(spec): |
| | | asset = AssetResolver(None).resolve(spec) |
| | | md5 = hashlib.md5() |
| | | with asset.stream() as stream: |
| | | for block in iter(lambda: stream.read(4096), b''): |
| | | md5.update(block) |
| | | return md5.hexdigest() |
| | | |
| | | class Md5AssetTokenGenerator(object): |
| | | """ |
| | | A mixin class which provides an implementation of |
| | | :meth:`~pyramid.interfaces.ICacheBuster.target` which generates an md5 |
| | | checksum token for an asset, caching it for subsequent calls. |
| | | """ |
| | | def __init__(self): |
| | | self.token_cache = {} |
| | | |
| | | def tokenize(self, pathspec): |
| | | # An astute observer will notice that this use of token_cache doesn't |
| | | # look particularly thread safe. Basic read/write operations on Python |
| | | # dicts, however, are atomic, so simply accessing and writing values |
| | | # to the dict shouldn't cause a segfault or other catastrophic failure. |
| | | # (See: http://effbot.org/pyfaq/what-kinds-of-global-value-mutation-are-thread-safe.htm) |
| | | # |
| | | # We do have a race condition that could result in the same md5 |
| | | # checksum getting computed twice or more times in parallel. Since |
| | | # the program would still function just fine if this were to occur, |
| | | # the extra overhead of using locks to serialize access to the dict |
| | | # seems an unnecessary burden. |
| | | # |
| | | token = self.token_cache.get(pathspec) |
| | | if not token: |
| | | self.token_cache[pathspec] = token = _generate_md5(pathspec) |
| | | return token |
| | | |
| | | class PathSegmentCacheBuster(object): |
| | | """ |
| | | An implementation of :class:`~pyramid.interfaces.ICacheBuster` which |
| | | inserts a token for cache busting in the path portion of an asset URL. |
| | | |
| | | To use this class, subclass it and provide a ``tokenize`` method which |
| | | accepts a ``pathspec`` and returns a token. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def pregenerate(self, pathspec, subpath, kw): |
| | | token = self.tokenize(pathspec) |
| | | return (token,) + subpath, kw |
| | | |
| | | def match(self, subpath): |
| | | return subpath[1:] |
| | | |
| | | class PathSegmentMd5CacheBuster(PathSegmentCacheBuster, |
| | | Md5AssetTokenGenerator): |
| | | """ |
| | | An implementation of :class:`~pyramid.interfaces.ICacheBuster` which |
| | | inserts an md5 checksum token for cache busting in the path portion of an |
| | | asset URL. Generated md5 checksums are cached in order to speed up |
| | | subsequent calls. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def __init__(self): |
| | | super(PathSegmentMd5CacheBuster, self).__init__() |
| | | |
| | | class QueryStringCacheBuster(object): |
| | | """ |
| | | An implementation of :class:`~pyramid.interfaces.ICacheBuster` which adds |
| | |
| | | to the query string and defaults to ``'x'``. |
| | | |
| | | To use this class, subclass it and provide a ``tokenize`` method which |
| | | accepts a ``pathspec`` and returns a token. |
| | | accepts ``request, pathspec, kw`` and returns a token. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def __init__(self, param='x'): |
| | | self.param = param |
| | | |
| | | def pregenerate(self, pathspec, subpath, kw): |
| | | token = self.tokenize(pathspec) |
| | | def __call__(self, request, subpath, kw): |
| | | token = self.tokenize(request, subpath, kw) |
| | | query = kw.setdefault('_query', {}) |
| | | if isinstance(query, dict): |
| | | query[self.param] = token |
| | | else: |
| | | kw['_query'] = tuple(query) + ((self.param, token),) |
| | | return subpath, kw |
| | | |
| | | class QueryStringMd5CacheBuster(QueryStringCacheBuster, |
| | | Md5AssetTokenGenerator): |
| | | """ |
| | | An implementation of :class:`~pyramid.interfaces.ICacheBuster` which adds |
| | | an md5 checksum token for cache busting in the query string of an asset |
| | | URL. Generated md5 checksums are cached in order to speed up subsequent |
| | | calls. |
| | | |
| | | The optional ``param`` argument determines the name of the parameter added |
| | | to the query string and defaults to ``'x'``. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | def __init__(self, param='x'): |
| | | super(QueryStringMd5CacheBuster, self).__init__(param=param) |
| | | |
| | | class QueryStringConstantCacheBuster(QueryStringCacheBuster): |
| | | """ |
| | |
| | | super(QueryStringConstantCacheBuster, self).__init__(param=param) |
| | | self._token = token |
| | | |
| | | def tokenize(self, pathspec): |
| | | def tokenize(self, request, subpath, kw): |
| | | return self._token |
| | | |
| | | class ManifestCacheBuster(object): |
| | | """ |
| | | An implementation of :class:`~pyramid.interfaces.ICacheBuster` which |
| | | uses a supplied manifest file to map an asset path to a cache-busted |
| | | version of the path. |
| | | |
| | | The ``manifest_spec`` can be an absolute path or a :term:`asset |
| | | specification` pointing to a package-relative file. |
| | | |
| | | The manifest file is expected to conform to the following simple JSON |
| | | format: |
| | | |
| | | .. code-block:: json |
| | | |
| | | { |
| | | "css/main.css": "css/main-678b7c80.css", |
| | | "images/background.png": "images/background-a8169106.png", |
| | | } |
| | | |
| | | By default, it is a JSON-serialized dictionary where the keys are the |
| | | source asset paths used in calls to |
| | | :meth:`~pyramid.request.Request.static_url`. For example:: |
| | | |
| | | .. code-block:: python |
| | | |
| | | >>> request.static_url('myapp:static/css/main.css') |
| | | "http://www.example.com/static/css/main-678b7c80.css" |
| | | |
| | | The file format and location can be changed by subclassing and overriding |
| | | :meth:`.parse_manifest`. |
| | | |
| | | If a path is not found in the manifest it will pass through unchanged. |
| | | |
| | | If ``reload`` is ``True`` then the manifest file will be reloaded when |
| | | changed. It is not recommended to leave this enabled in production. |
| | | |
| | | If the manifest file cannot be found on disk it will be treated as |
| | | an empty mapping unless ``reload`` is ``False``. |
| | | |
| | | .. versionadded:: 1.6 |
| | | """ |
| | | exists = staticmethod(exists) # testing |
| | | getmtime = staticmethod(getmtime) # testing |
| | | |
| | | def __init__(self, manifest_spec, reload=False): |
| | | package_name = caller_package().__name__ |
| | | self.manifest_path = abspath_from_asset_spec( |
| | | manifest_spec, package_name) |
| | | self.reload = reload |
| | | |
| | | self._mtime = None |
| | | if not reload: |
| | | self._manifest = self.get_manifest() |
| | | |
| | | def get_manifest(self): |
| | | with open(self.manifest_path, 'rb') as fp: |
| | | return self.parse_manifest(fp.read()) |
| | | |
| | | def parse_manifest(self, content): |
| | | """ |
| | | Parse the ``content`` read from the ``manifest_path`` into a |
| | | dictionary mapping. |
| | | |
| | | Subclasses may override this method to use something other than |
| | | ``json.loads`` to load any type of file format and return a conforming |
| | | dictionary. |
| | | |
| | | """ |
| | | return json.loads(content.decode('utf-8')) |
| | | |
| | | @property |
| | | def manifest(self): |
| | | """ The current manifest dictionary.""" |
| | | if self.reload: |
| | | if not self.exists(self.manifest_path): |
| | | return {} |
| | | mtime = self.getmtime(self.manifest_path) |
| | | if self._mtime is None or mtime > self._mtime: |
| | | self._manifest = self.get_manifest() |
| | | self._mtime = mtime |
| | | return self._manifest |
| | | |
| | | def __call__(self, request, subpath, kw): |
| | | subpath = self.manifest.get(subpath, subpath) |
| | | return (subpath, kw) |
New file |
| | |
| | | { |
| | | "css/main.css": "css/main-test.css", |
| | | "images/background.png": "images/background-a8169106.png" |
| | | } |
New file |
| | |
| | | { |
| | | "css/main.css": "css/main-678b7c80.css", |
| | | "images/background.png": "images/background-a8169106.png" |
| | | } |
| | |
| | | cookies.load(cookie) |
| | | return cookies.get('auth_tkt') |
| | | |
| | | def test_init_cookie_str_reissue_invalid(self): |
| | | self.assertRaises(ValueError, self._makeOne, 'secret', reissue_time='invalid value') |
| | | |
| | | def test_init_cookie_str_timeout_invalid(self): |
| | | self.assertRaises(ValueError, self._makeOne, 'secret', timeout='invalid value') |
| | | |
| | | def test_init_cookie_str_max_age_invalid(self): |
| | | self.assertRaises(ValueError, self._makeOne, 'secret', max_age='invalid value') |
| | | |
| | | def test_identify_nocookie(self): |
| | | helper = self._makeOne('secret') |
| | | request = self._makeRequest() |
| | |
| | | result = helper.identify(request) |
| | | self.assertEqual(result, None) |
| | | |
| | | def test_identify_cookie_timed_out(self): |
| | | def test_identify_cookie_timeout(self): |
| | | helper = self._makeOne('secret', timeout=1) |
| | | request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'}) |
| | | self.assertEqual(helper.timeout, 1) |
| | | |
| | | def test_identify_cookie_str_timeout(self): |
| | | helper = self._makeOne('secret', timeout='1') |
| | | self.assertEqual(helper.timeout, 1) |
| | | |
| | | def test_identify_cookie_timeout_aged(self): |
| | | import time |
| | | helper = self._makeOne('secret', timeout=10) |
| | | now = time.time() |
| | | helper.auth_tkt.timestamp = now - 1 |
| | | helper.now = now + 10 |
| | | helper.auth_tkt.tokens = (text_('a'), ) |
| | | request = self._makeRequest('bogus') |
| | | result = helper.identify(request) |
| | | self.assertEqual(result, None) |
| | | self.assertFalse(result) |
| | | |
| | | def test_identify_cookie_reissue(self): |
| | | import time |
| | | helper = self._makeOne('secret', timeout=10, reissue_time=0) |
| | | now = time.time() |
| | | helper.auth_tkt.timestamp = now |
| | | helper.now = now + 1 |
| | | helper.auth_tkt.tokens = (text_('a'), ) |
| | | request = self._makeRequest('bogus') |
| | | result = helper.identify(request) |
| | | self.assertTrue(result) |
| | | self.assertEqual(len(request.callbacks), 1) |
| | | response = DummyResponse() |
| | | request.callbacks[0](request, response) |
| | | self.assertEqual(len(response.headerlist), 3) |
| | | self.assertEqual(response.headerlist[0][0], 'Set-Cookie') |
| | | |
| | | def test_identify_cookie_str_reissue(self): |
| | | import time |
| | | helper = self._makeOne('secret', timeout=10, reissue_time='0') |
| | | now = time.time() |
| | | helper.auth_tkt.timestamp = now |
| | | helper.now = now + 1 |
| | |
| | | def test_remember_max_age(self): |
| | | helper = self._makeOne('secret') |
| | | request = self._makeRequest() |
| | | result = helper.remember(request, 'userid', max_age=500) |
| | | values = self._parseHeaders(result) |
| | | self.assertEqual(len(result), 3) |
| | | |
| | | self.assertEqual(values[0]['max-age'], '500') |
| | | self.assertTrue(values[0]['expires']) |
| | | |
| | | def test_remember_str_max_age(self): |
| | | helper = self._makeOne('secret') |
| | | request = self._makeRequest() |
| | | result = helper.remember(request, 'userid', max_age='500') |
| | | values = self._parseHeaders(result) |
| | | self.assertEqual(len(result), 3) |
| | | |
| | | self.assertEqual(values[0]['max-age'], '500') |
| | | self.assertTrue(values[0]['expires']) |
| | | |
| | | def test_remember_str_max_age_invalid(self): |
| | | helper = self._makeOne('secret') |
| | | request = self._makeRequest() |
| | | self.assertRaises(ValueError, helper.remember, request, 'userid', max_age='invalid value') |
| | | |
| | | def test_remember_tokens(self): |
| | | helper = self._makeOne('secret') |
| | |
| | | 'cd7a2fa4910000000auserid!') |
| | | result = self._callFUT('secret', ticket, '2001:db8::1', 'sha256') |
| | | self.assertEqual(result, (10, 'userid', [''], '')) |
| | | pass |
| | | |
| | | class TestSessionAuthenticationPolicy(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | |
| | | import os |
| | | import unittest |
| | | from pyramid import testing |
| | | |
| | |
| | | def _makeOne(self): |
| | | return self._getTargetClass()() |
| | | |
| | | def _makeConfig(self, registrations=None): |
| | | config = DummyConfig() |
| | | registry = DummyRegistry() |
| | | if registrations is not None: |
| | | registry._static_url_registrations = registrations |
| | | config.registry = registry |
| | | return config |
| | | |
| | | def _makeRequest(self): |
| | | request = DummyRequest() |
| | | request.registry = DummyRegistry() |
| | | return request |
| | | |
| | | def _assertRegistrations(self, config, expected): |
| | | self.assertEqual(config.registry._static_url_registrations, expected) |
| | | |
| | | def test_verifyClass(self): |
| | | from pyramid.interfaces import IStaticURLInfo |
| | |
| | | |
| | | def test_generate_registration_miss(self): |
| | | inst = self._makeOne() |
| | | registrations = [ |
| | | (None, 'spec', 'route_name', None), |
| | | ('http://example.com/foo/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | inst.registrations = [ |
| | | (None, 'spec', 'route_name'), |
| | | ('http://example.com/foo/', 'package:path/', None)] |
| | | request = self._makeRequest() |
| | | result = inst.generate('package:path/abc', request) |
| | | self.assertEqual(result, 'http://example.com/foo/abc') |
| | | |
| | | def test_generate_registration_no_registry_on_request(self): |
| | | inst = self._makeOne() |
| | | registrations = [ |
| | | ('http://example.com/foo/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | request = self._makeRequest() |
| | | del request.registry |
| | | result = inst.generate('package:path/abc', request) |
| | | self.assertEqual(result, 'http://example.com/foo/abc') |
| | | |
| | | def test_generate_slash_in_name1(self): |
| | | inst = self._makeOne() |
| | | registrations = [ |
| | | ('http://example.com/foo/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | inst.registrations = [('http://example.com/foo/', 'package:path/', None)] |
| | | request = self._makeRequest() |
| | | result = inst.generate('package:path/abc', request) |
| | | self.assertEqual(result, 'http://example.com/foo/abc') |
| | | |
| | | def test_generate_slash_in_name2(self): |
| | | inst = self._makeOne() |
| | | registrations = [ |
| | | ('http://example.com/foo/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | inst.registrations = [('http://example.com/foo/', 'package:path/', None)] |
| | | request = self._makeRequest() |
| | | result = inst.generate('package:path/', request) |
| | | self.assertEqual(result, 'http://example.com/foo/') |
| | | |
| | | def test_generate_quoting(self): |
| | | from pyramid.interfaces import IStaticURLInfo |
| | | config = testing.setUp() |
| | | try: |
| | | config.add_static_view('images', path='mypkg:templates') |
| | | inst = self._makeOne() |
| | | request = testing.DummyRequest() |
| | | request.registry = config.registry |
| | | inst = config.registry.getUtility(IStaticURLInfo) |
| | | result = inst.generate('mypkg:templates/foo%2Fbar', request) |
| | | self.assertEqual(result, 'http://example.com/images/foo%252Fbar') |
| | | finally: |
| | |
| | | |
| | | def test_generate_route_url(self): |
| | | inst = self._makeOne() |
| | | registrations = [(None, 'package:path/', '__viewname/', None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | inst.registrations = [(None, 'package:path/', '__viewname/')] |
| | | def route_url(n, **kw): |
| | | self.assertEqual(n, '__viewname/') |
| | | self.assertEqual(kw, {'subpath':'abc', 'a':1}) |
| | |
| | | |
| | | def test_generate_url_unquoted_local(self): |
| | | inst = self._makeOne() |
| | | registrations = [(None, 'package:path/', '__viewname/', None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | inst.registrations = [(None, 'package:path/', '__viewname/')] |
| | | def route_url(n, **kw): |
| | | self.assertEqual(n, '__viewname/') |
| | | self.assertEqual(kw, {'subpath':'abc def', 'a':1}) |
| | |
| | | |
| | | def test_generate_url_quoted_remote(self): |
| | | inst = self._makeOne() |
| | | registrations = [('http://example.com/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | inst.registrations = [('http://example.com/', 'package:path/', None)] |
| | | request = self._makeRequest() |
| | | result = inst.generate('package:path/abc def', request, a=1) |
| | | self.assertEqual(result, 'http://example.com/abc%20def') |
| | | |
| | | def test_generate_url_with_custom_query(self): |
| | | inst = self._makeOne() |
| | | registrations = [('http://example.com/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | registrations = [('http://example.com/', 'package:path/', None)] |
| | | inst.registrations = registrations |
| | | request = self._makeRequest() |
| | | result = inst.generate('package:path/abc def', request, a=1, |
| | | _query='(openlayers)') |
| | |
| | | |
| | | def test_generate_url_with_custom_anchor(self): |
| | | inst = self._makeOne() |
| | | registrations = [('http://example.com/', 'package:path/', None, None)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | inst.registrations = [('http://example.com/', 'package:path/', None)] |
| | | request = self._makeRequest() |
| | | uc = text_(b'La Pe\xc3\xb1a', 'utf-8') |
| | | result = inst.generate('package:path/abc def', request, a=1, |
| | | _anchor=uc) |
| | | result = inst.generate('package:path/abc def', request, a=1, _anchor=uc) |
| | | self.assertEqual(result, |
| | | 'http://example.com/abc%20def#La%20Pe%C3%B1a') |
| | | |
| | | def test_generate_url_cachebust(self): |
| | | def cachebust(subpath, kw): |
| | | def cachebust(request, subpath, kw): |
| | | kw['foo'] = 'bar' |
| | | return 'foo' + '/' + subpath, kw |
| | | inst = self._makeOne() |
| | | registrations = [(None, 'package:path/', '__viewname', cachebust)] |
| | | inst._get_registrations = lambda *x: registrations |
| | | inst.registrations = [(None, 'package:path/', '__viewname')] |
| | | inst.cache_busters = [('package:path/', cachebust, False)] |
| | | request = self._makeRequest() |
| | | called = [False] |
| | | def route_url(n, **kw): |
| | | called[0] = True |
| | | self.assertEqual(n, '__viewname') |
| | | self.assertEqual(kw, {'subpath':'foo/abc', 'foo':'bar'}) |
| | | self.assertEqual(kw, {'subpath': 'foo/abc', 'foo': 'bar', |
| | | 'pathspec': 'package:path/abc', |
| | | 'rawspec': 'package:path/abc'}) |
| | | request.route_url = route_url |
| | | inst.generate('package:path/abc', request) |
| | | self.assertTrue(called[0]) |
| | | |
| | | def test_generate_url_cachebust_abspath(self): |
| | | here = os.path.dirname(__file__) + os.sep |
| | | def cachebust(pathspec, subpath, kw): |
| | | kw['foo'] = 'bar' |
| | | return 'foo' + '/' + subpath, kw |
| | | inst = self._makeOne() |
| | | inst.registrations = [(None, here, '__viewname')] |
| | | inst.cache_busters = [(here, cachebust, False)] |
| | | request = self._makeRequest() |
| | | called = [False] |
| | | def route_url(n, **kw): |
| | | called[0] = True |
| | | self.assertEqual(n, '__viewname') |
| | | self.assertEqual(kw, {'subpath': 'foo/abc', 'foo': 'bar', |
| | | 'pathspec': here + 'abc', |
| | | 'rawspec': here + 'abc'}) |
| | | request.route_url = route_url |
| | | inst.generate(here + 'abc', request) |
| | | self.assertTrue(called[0]) |
| | | |
| | | def test_generate_url_cachebust_nomatch(self): |
| | | def fake_cb(*a, **kw): raise AssertionError |
| | | inst = self._makeOne() |
| | | inst.registrations = [(None, 'package:path/', '__viewname')] |
| | | inst.cache_busters = [('package:path2/', fake_cb, False)] |
| | | request = self._makeRequest() |
| | | called = [False] |
| | | def route_url(n, **kw): |
| | | called[0] = True |
| | | self.assertEqual(n, '__viewname') |
| | | self.assertEqual(kw, {'subpath': 'abc', |
| | | 'pathspec': 'package:path/abc', |
| | | 'rawspec': 'package:path/abc'}) |
| | | request.route_url = route_url |
| | | inst.generate('package:path/abc', request) |
| | | self.assertTrue(called[0]) |
| | | |
| | | def test_generate_url_cachebust_with_overrides(self): |
| | | config = testing.setUp() |
| | | try: |
| | | request = testing.DummyRequest() |
| | | config.add_static_view('static', 'path') |
| | | config.override_asset( |
| | | 'pyramid.tests.test_config:path/', |
| | | 'pyramid.tests.test_config:other_path/') |
| | | def cb(val): |
| | | def cb_(request, subpath, kw): |
| | | kw['_query'] = {'x': val} |
| | | return subpath, kw |
| | | return cb_ |
| | | config.add_cache_buster('path', cb('foo')) |
| | | result = request.static_url('path/foo.png') |
| | | self.assertEqual(result, 'http://example.com/static/foo.png?x=foo') |
| | | config.add_cache_buster('other_path', cb('bar'), explicit=True) |
| | | result = request.static_url('path/foo.png') |
| | | self.assertEqual(result, 'http://example.com/static/foo.png?x=bar') |
| | | finally: |
| | | testing.tearDown() |
| | | |
| | | def test_add_already_exists(self): |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | config = self._makeConfig( |
| | | [('http://example.com/', 'package:path/', None)]) |
| | | inst.registrations = [('http://example.com/', 'package:path/', None)] |
| | | inst.add(config, 'http://example.com', 'anotherpackage:path') |
| | | expected = [ |
| | | ('http://example.com/', 'anotherpackage:path/', None, None)] |
| | | self._assertRegistrations(config, expected) |
| | | expected = [('http://example.com/', 'anotherpackage:path/', None)] |
| | | self.assertEqual(inst.registrations, expected) |
| | | |
| | | def test_add_package_root(self): |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | config = self._makeConfig() |
| | | inst.add(config, 'http://example.com', 'package:') |
| | | expected = [('http://example.com/', 'package:', None, None)] |
| | | self._assertRegistrations(config, expected) |
| | | expected = [('http://example.com/', 'package:', None)] |
| | | self.assertEqual(inst.registrations, expected) |
| | | |
| | | def test_add_url_withendslash(self): |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | config = self._makeConfig() |
| | | inst.add(config, 'http://example.com/', 'anotherpackage:path') |
| | | expected = [ |
| | | ('http://example.com/', 'anotherpackage:path/', None, None)] |
| | | self._assertRegistrations(config, expected) |
| | | expected = [('http://example.com/', 'anotherpackage:path/', None)] |
| | | self.assertEqual(inst.registrations, expected) |
| | | |
| | | def test_add_url_noendslash(self): |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | config = self._makeConfig() |
| | | inst.add(config, 'http://example.com', 'anotherpackage:path') |
| | | expected = [ |
| | | ('http://example.com/', 'anotherpackage:path/', None, None)] |
| | | self._assertRegistrations(config, expected) |
| | | expected = [('http://example.com/', 'anotherpackage:path/', None)] |
| | | self.assertEqual(inst.registrations, expected) |
| | | |
| | | def test_add_url_noscheme(self): |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | config = self._makeConfig() |
| | | inst.add(config, '//example.com', 'anotherpackage:path') |
| | | expected = [('//example.com/', 'anotherpackage:path/', None, None)] |
| | | self._assertRegistrations(config, expected) |
| | | expected = [('//example.com/', 'anotherpackage:path/', None)] |
| | | self.assertEqual(inst.registrations, expected) |
| | | |
| | | def test_add_viewname(self): |
| | | from pyramid.security import NO_PERMISSION_REQUIRED |
| | | from pyramid.static import static_view |
| | | config = self._makeConfig() |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1) |
| | | expected = [(None, 'anotherpackage:path/', '__view/', None)] |
| | | self._assertRegistrations(config, expected) |
| | | expected = [(None, 'anotherpackage:path/', '__view/')] |
| | | self.assertEqual(inst.registrations, expected) |
| | | self.assertEqual(config.route_args, ('__view/', 'view/*subpath')) |
| | | self.assertEqual(config.view_kw['permission'], NO_PERMISSION_REQUIRED) |
| | | self.assertEqual(config.view_kw['view'].__class__, static_view) |
| | | |
| | | def test_add_viewname_with_route_prefix(self): |
| | | config = self._makeConfig() |
| | | config = DummyConfig() |
| | | config.route_prefix = '/abc' |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'anotherpackage:path',) |
| | | expected = [(None, 'anotherpackage:path/', '__/abc/view/', None)] |
| | | self._assertRegistrations(config, expected) |
| | | expected = [(None, 'anotherpackage:path/', '__/abc/view/')] |
| | | self.assertEqual(inst.registrations, expected) |
| | | self.assertEqual(config.route_args, ('__/abc/view/', 'view/*subpath')) |
| | | |
| | | def test_add_viewname_with_permission(self): |
| | | config = self._makeConfig() |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, |
| | | permission='abc') |
| | | self.assertEqual(config.view_kw['permission'], 'abc') |
| | | |
| | | def test_add_viewname_with_context(self): |
| | | config = self._makeConfig() |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, |
| | | context=DummyContext) |
| | | self.assertEqual(config.view_kw['context'], DummyContext) |
| | | |
| | | def test_add_viewname_with_for_(self): |
| | | config = self._makeConfig() |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, |
| | | for_=DummyContext) |
| | | self.assertEqual(config.view_kw['context'], DummyContext) |
| | | |
| | | def test_add_viewname_with_renderer(self): |
| | | config = self._makeConfig() |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'anotherpackage:path', cache_max_age=1, |
| | | renderer='mypackage:templates/index.pt') |
| | | self.assertEqual(config.view_kw['renderer'], |
| | | 'mypackage:templates/index.pt') |
| | | |
| | | def test_add_cachebust_default(self): |
| | | config = self._makeConfig() |
| | | inst = self._makeOne() |
| | | inst._default_cachebust = lambda: DummyCacheBuster('foo') |
| | | inst.add(config, 'view', 'mypackage:path', cachebust=True) |
| | | cachebust = config.registry._static_url_registrations[0][3] |
| | | subpath, kw = cachebust('some/path', {}) |
| | | self.assertEqual(subpath, 'some/path') |
| | | self.assertEqual(kw['x'], 'foo') |
| | | |
| | | def test_add_cachebust_prevented(self): |
| | | config = self._makeConfig() |
| | | config = DummyConfig() |
| | | config.registry.settings['pyramid.prevent_cachebust'] = True |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'mypackage:path', cachebust=True) |
| | | cachebust = config.registry._static_url_registrations[0][3] |
| | | self.assertEqual(cachebust, None) |
| | | cachebust = DummyCacheBuster('foo') |
| | | inst.add_cache_buster(config, 'mypackage:path', cachebust) |
| | | self.assertEqual(inst.cache_busters, []) |
| | | |
| | | def test_add_cachebust_custom(self): |
| | | config = self._makeConfig() |
| | | def test_add_cachebuster(self): |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | inst.add(config, 'view', 'mypackage:path', |
| | | cachebust=DummyCacheBuster('foo')) |
| | | cachebust = config.registry._static_url_registrations[0][3] |
| | | subpath, kw = cachebust('some/path', {}) |
| | | inst.add_cache_buster(config, 'mypackage:path', DummyCacheBuster('foo')) |
| | | cachebust = inst.cache_busters[-1][1] |
| | | subpath, kw = cachebust(None, 'some/path', {}) |
| | | self.assertEqual(subpath, 'some/path') |
| | | self.assertEqual(kw['x'], 'foo') |
| | | |
| | | def test_add_cachebuster_abspath(self): |
| | | here = os.path.dirname(__file__) |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | cb = DummyCacheBuster('foo') |
| | | inst.add_cache_buster(config, here, cb) |
| | | self.assertEqual(inst.cache_busters, [(here + '/', cb, False)]) |
| | | |
| | | def test_add_cachebuster_overwrite(self): |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | cb1 = DummyCacheBuster('foo') |
| | | cb2 = DummyCacheBuster('bar') |
| | | inst.add_cache_buster(config, 'mypackage:path/', cb1) |
| | | inst.add_cache_buster(config, 'mypackage:path', cb2) |
| | | self.assertEqual(inst.cache_busters, |
| | | [('mypackage:path/', cb2, False)]) |
| | | |
| | | def test_add_cachebuster_overwrite_explicit(self): |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | cb1 = DummyCacheBuster('foo') |
| | | cb2 = DummyCacheBuster('bar') |
| | | inst.add_cache_buster(config, 'mypackage:path/', cb1) |
| | | inst.add_cache_buster(config, 'mypackage:path', cb2, True) |
| | | self.assertEqual(inst.cache_busters, |
| | | [('mypackage:path/', cb1, False), |
| | | ('mypackage:path/', cb2, True)]) |
| | | |
| | | def test_add_cachebuster_for_more_specific_path(self): |
| | | config = DummyConfig() |
| | | inst = self._makeOne() |
| | | cb1 = DummyCacheBuster('foo') |
| | | cb2 = DummyCacheBuster('bar') |
| | | cb3 = DummyCacheBuster('baz') |
| | | cb4 = DummyCacheBuster('xyz') |
| | | cb5 = DummyCacheBuster('w') |
| | | inst.add_cache_buster(config, 'mypackage:path', cb1) |
| | | inst.add_cache_buster(config, 'mypackage:path/sub', cb2, True) |
| | | inst.add_cache_buster(config, 'mypackage:path/sub/other', cb3) |
| | | inst.add_cache_buster(config, 'mypackage:path/sub/other', cb4, True) |
| | | inst.add_cache_buster(config, 'mypackage:path/sub/less', cb5, True) |
| | | self.assertEqual( |
| | | inst.cache_busters, |
| | | [('mypackage:path/', cb1, False), |
| | | ('mypackage:path/sub/other/', cb3, False), |
| | | ('mypackage:path/sub/', cb2, True), |
| | | ('mypackage:path/sub/less/', cb5, True), |
| | | ('mypackage:path/sub/other/', cb4, True)]) |
| | | |
| | | class Test_view_description(unittest.TestCase): |
| | | def _callFUT(self, view): |
| | |
| | | |
| | | |
| | | class DummyRegistry: |
| | | utility = None |
| | | |
| | | def __init__(self): |
| | | self.settings = {} |
| | | |
| | | def queryUtility(self, type_or_iface, name=None, default=None): |
| | | return self.utility or default |
| | | |
| | | from zope.interface import implementer |
| | | from pyramid.interfaces import ( |
| | |
| | | return val in self.matches |
| | | |
| | | class DummyConfig: |
| | | def __init__(self): |
| | | self.registry = DummyRegistry() |
| | | |
| | | route_prefix = '' |
| | | def add_route(self, *args, **kw): |
| | | self.route_args = args |
| | |
| | | class DummyCacheBuster(object): |
| | | def __init__(self, token): |
| | | self.token = token |
| | | def pregenerate(self, pathspec, subpath, kw): |
| | | |
| | | def __call__(self, request, subpath, kw): |
| | | kw['x'] = self.token |
| | | return subpath, kw |
| | | |
| | |
| | | def __call__(self, context, request): |
| | | self.context = context |
| | | self.request = request |
| | | if not self.raise_exception is None: |
| | | if self.raise_exception is not None: |
| | | raise self.raise_exception |
| | | return self.response |
| | | |
| | |
| | | class DummyShell(object): |
| | | env = {} |
| | | help = '' |
| | | called = False |
| | | |
| | | def __call__(self, env, help): |
| | | self.env = env |
| | | self.help = help |
| | | self.called = True |
| | | |
| | | class DummyInteractor: |
| | | def __call__(self, banner, local): |
| | | self.banner = banner |
| | | self.local = local |
| | | |
| | | class DummyBPythonShell: |
| | | def __call__(self, locals_, banner): |
| | | self.locals_ = locals_ |
| | | self.banner = banner |
| | | |
| | | class DummyIPShell(object): |
| | | IP = Dummy() |
| | | IP.BANNER = 'foo' |
| | | |
| | | def __call__(self): |
| | | self.called = True |
| | | |
| | | class DummyIPShellFactory(object): |
| | | def __call__(self, **kw): |
| | | self.kw = kw |
| | | self.shell = DummyIPShell() |
| | | return self.shell |
| | | |
| | | class DummyApp: |
| | | def __init__(self): |
| | |
| | | self.options = Options() |
| | | self.options.python_shell = '' |
| | | self.options.setup = None |
| | | self.options.list = None |
| | | cmd.options = self.options |
| | | |
| | | # default to None to prevent side-effects from running tests in |
| | |
| | | def _makeEntryPoints(self, command, shells): |
| | | command.pkg_resources = dummy.DummyPkgResources(shells) |
| | | |
| | | def test_make_default_shell(self): |
| | | command = self._makeOne() |
| | | interact = dummy.DummyInteractor() |
| | | shell = command.make_default_shell(interact) |
| | | shell({'foo': 'bar'}, 'a help message') |
| | | self.assertEqual(interact.local, {'foo': 'bar'}) |
| | | self.assertTrue('a help message' in interact.banner) |
| | | |
| | | def test_make_bpython_shell(self): |
| | | command = self._makeOne() |
| | | bpython = dummy.DummyBPythonShell() |
| | | shell = command.make_bpython_shell(bpython) |
| | | shell({'foo': 'bar'}, 'a help message') |
| | | self.assertEqual(bpython.locals_, {'foo': 'bar'}) |
| | | self.assertTrue('a help message' in bpython.banner) |
| | | |
| | | def test_make_ipython_v1_1_shell(self): |
| | | command = self._makeOne() |
| | | ipshell_factory = dummy.DummyIPShellFactory() |
| | | shell = command.make_ipython_shell(ipshell_factory) |
| | | shell({'foo': 'bar'}, 'a help message') |
| | | self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'}) |
| | | self.assertTrue('a help message' in ipshell_factory.kw['banner2']) |
| | | self.assertTrue(ipshell_factory.shell.called) |
| | | |
| | | def test_command_loads_default_shell(self): |
| | | command = self._makeOne() |
| | | shell = dummy.DummyShell() |
| | | self._makeEntryPoints( |
| | | command, |
| | | { |
| | | 'ipython': lambda: None, |
| | | 'bpython': lambda: None, |
| | | } |
| | | ) |
| | | self._makeEntryPoints(command, {}) |
| | | |
| | | command.make_default_shell = lambda: shell |
| | | command.default_runner = shell |
| | | command.run() |
| | | self.assertTrue(self.config_factory.parser) |
| | | self.assertEqual(self.config_factory.parser.filename, |
| | |
| | | self.assertTrue(self.bootstrap.closer.called) |
| | | self.assertTrue(shell.help) |
| | | |
| | | def test_command_loads_default_shell_with_unknown_shell(self): |
| | | def test_command_errors_with_unknown_shell(self): |
| | | command = self._makeOne() |
| | | out_calls = [] |
| | | |
| | |
| | | command.out = out |
| | | |
| | | shell = dummy.DummyShell() |
| | | bad_shell = dummy.DummyShell() |
| | | |
| | | self._makeEntryPoints( |
| | | command, |
| | | { |
| | | 'ipython': lambda: bad_shell, |
| | | 'bpython': lambda: bad_shell, |
| | | } |
| | | ) |
| | | self._makeEntryPoints(command, {}) |
| | | |
| | | command.make_default_shell = lambda: shell |
| | | command.default_runner = shell |
| | | command.options.python_shell = 'unknown_python_shell' |
| | | result = command.run() |
| | | self.assertEqual(result, 1) |
| | |
| | | self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') |
| | | self.assertTrue(self.bootstrap.closer.called) |
| | | |
| | | def test_command_loads_ipython_v1_1(self): |
| | | def test_command_loads_ipython(self): |
| | | command = self._makeOne() |
| | | shell = dummy.DummyShell() |
| | | bad_shell = dummy.DummyShell() |
| | | self._makeEntryPoints( |
| | | command, |
| | | { |
| | | 'ipython': lambda: shell, |
| | | 'bpython': lambda: bad_shell, |
| | | 'ipython': shell, |
| | | 'bpython': bad_shell, |
| | | } |
| | | ) |
| | | |
| | |
| | | self.assertTrue(self.bootstrap.closer.called) |
| | | self.assertTrue(shell.help) |
| | | |
| | | def test_command_loads_bpython_shell(self): |
| | | command = self._makeOne() |
| | | shell = dummy.DummyBPythonShell() |
| | | |
| | | self._makeEntryPoints( |
| | | command, |
| | | { |
| | | 'ipython': lambda: None, |
| | | 'bpython': lambda: shell, |
| | | } |
| | | ) |
| | | |
| | | command.options.python_shell = 'bpython' |
| | | command.run() |
| | | self.assertTrue(self.config_factory.parser) |
| | | self.assertEqual(self.config_factory.parser.filename, |
| | | '/foo/bar/myapp.ini') |
| | | self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') |
| | | self.assertEqual(shell.locals_, { |
| | | 'app':self.bootstrap.app, 'root':self.bootstrap.root, |
| | | 'registry':self.bootstrap.registry, |
| | | 'request':self.bootstrap.request, |
| | | 'root_factory':self.bootstrap.root_factory, |
| | | }) |
| | | self.assertTrue(self.bootstrap.closer.called) |
| | | self.assertTrue(shell.banner) |
| | | |
| | | def test_shell_entry_points(self): |
| | | command = self._makeOne() |
| | | dshell = dummy.DummyShell() |
| | |
| | | self._makeEntryPoints( |
| | | command, |
| | | { |
| | | 'ipython': lambda: dshell, |
| | | 'bpython': lambda: dshell, |
| | | 'ipython': dshell, |
| | | 'bpython': dshell, |
| | | } |
| | | ) |
| | | |
| | | command.make_default_shell = lambda: None |
| | | command.default_runner = None |
| | | shell = command.make_shell() |
| | | self.assertEqual(shell, dshell) |
| | | |
| | | def test_shell_ordering(self): |
| | | def test_shell_override(self): |
| | | command = self._makeOne() |
| | | ipshell = dummy.DummyShell() |
| | | bpshell = dummy.DummyShell() |
| | | dshell = dummy.DummyShell() |
| | | |
| | | self._makeEntryPoints( |
| | | command, |
| | | { |
| | | 'ipython': lambda: None, |
| | | 'bpython': lambda: None, |
| | | } |
| | | ) |
| | | self._makeEntryPoints(command, {}) |
| | | |
| | | command.make_default_shell = lambda: dshell |
| | | command.default_runner = dshell |
| | | |
| | | shell = command.make_shell() |
| | | self.assertEqual(shell, dshell) |
| | | |
| | | command.options.python_shell = 'ipython' |
| | | shell = command.make_shell() |
| | | self.assertEqual(shell, dshell) |
| | | |
| | | command.options.python_shell = 'bpython' |
| | | shell = command.make_shell() |
| | | self.assertEqual(shell, dshell) |
| | | self.assertRaises(ValueError, command.make_shell) |
| | | |
| | | self._makeEntryPoints( |
| | | command, |
| | | { |
| | | 'ipython': lambda: ipshell, |
| | | 'bpython': lambda: bpshell, |
| | | 'python': lambda: dshell, |
| | | 'ipython': ipshell, |
| | | 'bpython': bpshell, |
| | | 'python': dshell, |
| | | } |
| | | ) |
| | | |
| | |
| | | shell = command.make_shell() |
| | | self.assertEqual(shell, dshell) |
| | | |
| | | def test_shell_ordering(self): |
| | | command = self._makeOne() |
| | | ipshell = dummy.DummyShell() |
| | | bpshell = dummy.DummyShell() |
| | | dshell = dummy.DummyShell() |
| | | |
| | | self._makeEntryPoints( |
| | | command, |
| | | { |
| | | 'ipython': ipshell, |
| | | 'bpython': bpshell, |
| | | 'python': dshell, |
| | | } |
| | | ) |
| | | |
| | | command.default_runner = dshell |
| | | |
| | | command.preferred_shells = ['ipython', 'bpython'] |
| | | shell = command.make_shell() |
| | | self.assertEqual(shell, ipshell) |
| | | |
| | | command.preferred_shells = ['bpython', 'python'] |
| | | shell = command.make_shell() |
| | | self.assertEqual(shell, bpshell) |
| | | |
| | | command.preferred_shells = ['python', 'ipython'] |
| | | shell = command.make_shell() |
| | | self.assertEqual(shell, dshell) |
| | | |
| | | def test_command_loads_custom_items(self): |
| | | command = self._makeOne() |
| | | model = dummy.Dummy() |
| | | self.config_factory.items = [('m', model)] |
| | | user = dummy.Dummy() |
| | | self.config_factory.items = [('m', model), ('User', user)] |
| | | shell = dummy.DummyShell() |
| | | command.run(shell) |
| | | self.assertTrue(self.config_factory.parser) |
| | |
| | | 'request':self.bootstrap.request, |
| | | 'root_factory':self.bootstrap.root_factory, |
| | | 'm':model, |
| | | 'User': user, |
| | | }) |
| | | self.assertTrue(self.bootstrap.closer.called) |
| | | self.assertTrue(shell.help) |
| | |
| | | }) |
| | | self.assertTrue(self.bootstrap.closer.called) |
| | | self.assertTrue(shell.help) |
| | | |
| | | def test_command_default_shell_option(self): |
| | | command = self._makeOne() |
| | | ipshell = dummy.DummyShell() |
| | | dshell = dummy.DummyShell() |
| | | self._makeEntryPoints( |
| | | command, |
| | | { |
| | | 'ipython': ipshell, |
| | | 'python': dshell, |
| | | } |
| | | ) |
| | | self.config_factory.items = [ |
| | | ('default_shell', 'bpython python\nipython')] |
| | | command.run() |
| | | self.assertTrue(self.config_factory.parser) |
| | | self.assertEqual(self.config_factory.parser.filename, |
| | | '/foo/bar/myapp.ini') |
| | | self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') |
| | | self.assertTrue(dshell.called) |
| | | |
| | | def test_command_loads_check_variable_override_order(self): |
| | | command = self._makeOne() |
| | |
| | | self.assertTrue(self.bootstrap.closer.called) |
| | | self.assertTrue(shell.help) |
| | | |
| | | def test_list_shells(self): |
| | | command = self._makeOne() |
| | | |
| | | dshell = dummy.DummyShell() |
| | | out_calls = [] |
| | | |
| | | def out(msg): |
| | | out_calls.append(msg) |
| | | |
| | | command.out = out |
| | | |
| | | self._makeEntryPoints( |
| | | command, |
| | | { |
| | | 'ipython': dshell, |
| | | 'python': dshell, |
| | | } |
| | | ) |
| | | |
| | | command.options.list = True |
| | | result = command.run() |
| | | self.assertEqual(result, 0) |
| | | self.assertEqual(out_calls, [ |
| | | 'Available shells:', |
| | | ' ipython', |
| | | ' python', |
| | | ]) |
| | | |
| | | |
| | | class Test_python_shell_runner(unittest.TestCase): |
| | | def _callFUT(self, env, help, interact): |
| | | from pyramid.scripts.pshell import python_shell_runner |
| | | return python_shell_runner(env, help, interact=interact) |
| | | |
| | | def test_it(self): |
| | | interact = dummy.DummyInteractor() |
| | | self._callFUT({'foo': 'bar'}, 'a help message', interact) |
| | | self.assertEqual(interact.local, {'foo': 'bar'}) |
| | | self.assertTrue('a help message' in interact.banner) |
| | | |
| | | class Test_main(unittest.TestCase): |
| | | def _callFUT(self, argv): |
| | | from pyramid.scripts.pshell import main |
| | |
| | | def test_it(self): |
| | | result = self._callFUT(['pshell']) |
| | | self.assertEqual(result, 2) |
| | | |
| | |
| | | session = self._makeOne(request, timeout=None) |
| | | self.assertEqual(dict(session), {'state': 1}) |
| | | |
| | | def test_timeout_str(self): |
| | | import time |
| | | request = testing.DummyRequest() |
| | | cookieval = self._serialize((time.time() - 5, 0, {'state': 1})) |
| | | request.cookies['session'] = cookieval |
| | | session = self._makeOne(request, timeout='1') |
| | | self.assertEqual(dict(session), {}) |
| | | |
| | | def test_timeout_invalid(self): |
| | | request = testing.DummyRequest() |
| | | self.assertRaises(ValueError, self._makeOne, request, timeout='Invalid value') |
| | | |
| | | def test_changed(self): |
| | | request = testing.DummyRequest() |
| | | session = self._makeOne(request) |
| | |
| | | self.assertEqual(session['state'], 1) |
| | | self.assertFalse(session._dirty) |
| | | |
| | | def test_reissue_str_triggered(self): |
| | | import time |
| | | request = testing.DummyRequest() |
| | | cookieval = self._serialize((time.time() - 2, 0, {'state': 1})) |
| | | request.cookies['session'] = cookieval |
| | | session = self._makeOne(request, reissue_time='0') |
| | | self.assertEqual(session['state'], 1) |
| | | self.assertTrue(session._dirty) |
| | | |
| | | def test_reissue_invalid(self): |
| | | request = testing.DummyRequest() |
| | | self.assertRaises(ValueError, self._makeOne, request, reissue_time='invalid value') |
| | | |
| | | def test_cookie_max_age_invalid(self): |
| | | request = testing.DummyRequest() |
| | | self.assertRaises(ValueError, self._makeOne, request, max_age='invalid value') |
| | | |
| | | class TestSignedCookieSession(SharedCookieSessionTests, unittest.TestCase): |
| | | def _makeOne(self, request, **kw): |
| | | from pyramid.session import SignedCookieSessionFactory |
| | |
| | | self.assertEqual(session['state'], 1) |
| | | self.assertFalse(session._dirty) |
| | | |
| | | def test_reissue_str_triggered(self): |
| | | import time |
| | | request = testing.DummyRequest() |
| | | cookieval = self._serialize((time.time() - 2, 0, {'state': 1})) |
| | | request.cookies['session'] = cookieval |
| | | session = self._makeOne(request, reissue_time='0') |
| | | self.assertEqual(session['state'], 1) |
| | | self.assertTrue(session._dirty) |
| | | |
| | | def test_reissue_invalid(self): |
| | | request = testing.DummyRequest() |
| | | self.assertRaises(ValueError, self._makeOne, request, reissue_time='invalid value') |
| | | |
| | | def test_cookie_max_age_invalid(self): |
| | | request = testing.DummyRequest() |
| | | self.assertRaises(ValueError, self._makeOne, request, max_age='invalid value') |
| | | |
| | | def test_custom_salt(self): |
| | | import time |
| | | request = testing.DummyRequest() |
| | |
| | | import datetime |
| | | import os.path |
| | | import unittest |
| | | |
| | | here = os.path.dirname(__file__) |
| | | |
| | | # 5 years from now (more or less) |
| | | fiveyrsfuture = datetime.datetime.utcnow() + datetime.timedelta(5*365) |
| | |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | self.assertRaises(HTTPNotFound, inst, context, request) |
| | | |
| | | class TestMd5AssetTokenGenerator(unittest.TestCase): |
| | | _fspath = None |
| | | _tmp = None |
| | | |
| | | @property |
| | | def fspath(self): |
| | | if self._fspath: |
| | | return self._fspath |
| | | |
| | | import os |
| | | import tempfile |
| | | self._tmp = tmp = tempfile.mkdtemp() |
| | | self._fspath = os.path.join(tmp, 'test.txt') |
| | | return self._fspath |
| | | |
| | | def tearDown(self): |
| | | import shutil |
| | | if self._tmp: |
| | | shutil.rmtree(self._tmp) |
| | | |
| | | def _makeOne(self): |
| | | from pyramid.static import Md5AssetTokenGenerator as cls |
| | | return cls() |
| | | |
| | | def test_package_resource(self): |
| | | fut = self._makeOne().tokenize |
| | | expected = '76d653a3a044e2f4b38bb001d283e3d9' |
| | | token = fut('pyramid.tests:fixtures/static/index.html') |
| | | self.assertEqual(token, expected) |
| | | |
| | | def test_filesystem_resource(self): |
| | | fut = self._makeOne().tokenize |
| | | expected = 'd5155f250bef0e9923e894dbc713c5dd' |
| | | with open(self.fspath, 'w') as f: |
| | | f.write("Are we rich yet?") |
| | | token = fut(self.fspath) |
| | | self.assertEqual(token, expected) |
| | | |
| | | def test_cache(self): |
| | | fut = self._makeOne().tokenize |
| | | expected = 'd5155f250bef0e9923e894dbc713c5dd' |
| | | with open(self.fspath, 'w') as f: |
| | | f.write("Are we rich yet?") |
| | | token = fut(self.fspath) |
| | | self.assertEqual(token, expected) |
| | | |
| | | # md5 shouldn't change because we've cached it |
| | | with open(self.fspath, 'w') as f: |
| | | f.write("Sorry for the convenience.") |
| | | token = fut(self.fspath) |
| | | self.assertEqual(token, expected) |
| | | |
| | | class TestPathSegmentMd5CacheBuster(unittest.TestCase): |
| | | |
| | | def _makeOne(self): |
| | | from pyramid.static import PathSegmentMd5CacheBuster as cls |
| | | inst = cls() |
| | | inst.tokenize = lambda pathspec: 'foo' |
| | | return inst |
| | | |
| | | def test_token(self): |
| | | fut = self._makeOne().tokenize |
| | | self.assertEqual(fut('whatever'), 'foo') |
| | | |
| | | def test_pregenerate(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual(fut('foo', ('bar',), 'kw'), (('foo', 'bar'), 'kw')) |
| | | |
| | | def test_match(self): |
| | | fut = self._makeOne().match |
| | | self.assertEqual(fut(('foo', 'bar')), ('bar',)) |
| | | |
| | | class TestQueryStringMd5CacheBuster(unittest.TestCase): |
| | | |
| | | def _makeOne(self, param=None): |
| | | from pyramid.static import QueryStringMd5CacheBuster as cls |
| | | if param: |
| | | inst = cls(param) |
| | | else: |
| | | inst = cls() |
| | | inst.tokenize = lambda pathspec: 'foo' |
| | | return inst |
| | | |
| | | def test_token(self): |
| | | fut = self._makeOne().tokenize |
| | | self.assertEqual(fut('whatever'), 'foo') |
| | | |
| | | def test_pregenerate(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {}), |
| | | (('bar',), {'_query': {'x': 'foo'}})) |
| | | |
| | | def test_pregenerate_change_param(self): |
| | | fut = self._makeOne('y').pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {}), |
| | | (('bar',), {'_query': {'y': 'foo'}})) |
| | | |
| | | def test_pregenerate_query_is_already_tuples(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {'_query': [('a', 'b')]}), |
| | | (('bar',), {'_query': (('a', 'b'), ('x', 'foo'))})) |
| | | |
| | | def test_pregenerate_query_is_tuple_of_tuples(self): |
| | | fut = self._makeOne().pregenerate |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {'_query': (('a', 'b'),)}), |
| | | (('bar',), {'_query': (('a', 'b'), ('x', 'foo'))})) |
| | | |
| | | class TestQueryStringConstantCacheBuster(TestQueryStringMd5CacheBuster): |
| | | class TestQueryStringConstantCacheBuster(unittest.TestCase): |
| | | |
| | | def _makeOne(self, param=None): |
| | | from pyramid.static import QueryStringConstantCacheBuster as cls |
| | |
| | | |
| | | def test_token(self): |
| | | fut = self._makeOne().tokenize |
| | | self.assertEqual(fut('whatever'), 'foo') |
| | | self.assertEqual(fut(None, 'whatever', None), 'foo') |
| | | |
| | | def test_pregenerate(self): |
| | | fut = self._makeOne().pregenerate |
| | | def test_it(self): |
| | | fut = self._makeOne() |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {}), |
| | | (('bar',), {'_query': {'x': 'foo'}})) |
| | | fut('foo', 'bar', {}), |
| | | ('bar', {'_query': {'x': 'foo'}})) |
| | | |
| | | def test_pregenerate_change_param(self): |
| | | fut = self._makeOne('y').pregenerate |
| | | def test_change_param(self): |
| | | fut = self._makeOne('y') |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {}), |
| | | (('bar',), {'_query': {'y': 'foo'}})) |
| | | fut('foo', 'bar', {}), |
| | | ('bar', {'_query': {'y': 'foo'}})) |
| | | |
| | | def test_pregenerate_query_is_already_tuples(self): |
| | | fut = self._makeOne().pregenerate |
| | | def test_query_is_already_tuples(self): |
| | | fut = self._makeOne() |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {'_query': [('a', 'b')]}), |
| | | (('bar',), {'_query': (('a', 'b'), ('x', 'foo'))})) |
| | | fut('foo', 'bar', {'_query': [('a', 'b')]}), |
| | | ('bar', {'_query': (('a', 'b'), ('x', 'foo'))})) |
| | | |
| | | def test_pregenerate_query_is_tuple_of_tuples(self): |
| | | fut = self._makeOne().pregenerate |
| | | def test_query_is_tuple_of_tuples(self): |
| | | fut = self._makeOne() |
| | | self.assertEqual( |
| | | fut('foo', ('bar',), {'_query': (('a', 'b'),)}), |
| | | (('bar',), {'_query': (('a', 'b'), ('x', 'foo'))})) |
| | | fut('foo', 'bar', {'_query': (('a', 'b'),)}), |
| | | ('bar', {'_query': (('a', 'b'), ('x', 'foo'))})) |
| | | |
| | | class TestManifestCacheBuster(unittest.TestCase): |
| | | |
| | | def _makeOne(self, path, **kw): |
| | | from pyramid.static import ManifestCacheBuster as cls |
| | | return cls(path, **kw) |
| | | |
| | | def test_it(self): |
| | | manifest_path = os.path.join(here, 'fixtures', 'manifest.json') |
| | | fut = self._makeOne(manifest_path) |
| | | self.assertEqual(fut('foo', 'bar', {}), ('bar', {})) |
| | | self.assertEqual( |
| | | fut('foo', 'css/main.css', {}), |
| | | ('css/main-test.css', {})) |
| | | |
| | | def test_it_with_relspec(self): |
| | | fut = self._makeOne('fixtures/manifest.json') |
| | | self.assertEqual(fut('foo', 'bar', {}), ('bar', {})) |
| | | self.assertEqual( |
| | | fut('foo', 'css/main.css', {}), |
| | | ('css/main-test.css', {})) |
| | | |
| | | def test_it_with_absspec(self): |
| | | fut = self._makeOne('pyramid.tests:fixtures/manifest.json') |
| | | self.assertEqual(fut('foo', 'bar', {}), ('bar', {})) |
| | | self.assertEqual( |
| | | fut('foo', 'css/main.css', {}), |
| | | ('css/main-test.css', {})) |
| | | |
| | | def test_reload(self): |
| | | manifest_path = os.path.join(here, 'fixtures', 'manifest.json') |
| | | new_manifest_path = os.path.join(here, 'fixtures', 'manifest2.json') |
| | | inst = self._makeOne('foo', reload=True) |
| | | inst.getmtime = lambda *args, **kwargs: 0 |
| | | fut = inst |
| | | |
| | | # test without a valid manifest |
| | | self.assertEqual( |
| | | fut('foo', 'css/main.css', {}), |
| | | ('css/main.css', {})) |
| | | |
| | | # swap to a real manifest, setting mtime to 0 |
| | | inst.manifest_path = manifest_path |
| | | self.assertEqual( |
| | | fut('foo', 'css/main.css', {}), |
| | | ('css/main-test.css', {})) |
| | | |
| | | # ensure switching the path doesn't change the result |
| | | inst.manifest_path = new_manifest_path |
| | | self.assertEqual( |
| | | fut('foo', 'css/main.css', {}), |
| | | ('css/main-test.css', {})) |
| | | |
| | | # update mtime, should cause a reload |
| | | inst.getmtime = lambda *args, **kwargs: 1 |
| | | self.assertEqual( |
| | | fut('foo', 'css/main.css', {}), |
| | | ('css/main-678b7c80.css', {})) |
| | | |
| | | def test_invalid_manifest(self): |
| | | self.assertRaises(IOError, lambda: self._makeOne('foo')) |
| | | |
| | | def test_invalid_manifest_with_reload(self): |
| | | inst = self._makeOne('foo', reload=True) |
| | | self.assertEqual(inst.manifest, {}) |
| | | |
| | | class DummyContext: |
| | | pass |
| | |
| | | if scheme == 'http': |
| | | if port is None: |
| | | port = '80' |
| | | url = scheme + '://' |
| | | if port is not None: |
| | | port = str(port) |
| | | if host is None: |
| | | host = e.get('HTTP_HOST') |
| | | if host is None: |
| | | host = e['SERVER_NAME'] |
| | | if host is None: |
| | | host = e['SERVER_NAME'] |
| | | if port is None: |
| | | if ':' in host: |
| | | host, port = host.split(':', 1) |
| | | else: |
| | | port = e['SERVER_PORT'] |
| | | else: |
| | | port = str(port) |
| | | if ':' in host: |
| | | host, _ = host.split(':', 1) |
| | | if scheme == 'https': |
| | |
| | | elif scheme == 'http': |
| | | if port == '80': |
| | | port = None |
| | | url += host |
| | | url = scheme + '://' + host |
| | | if port: |
| | | url += ':%s' % port |
| | | |
| | |
| | | |
| | | class DottedNameResolver(_DottedNameResolver): |
| | | def __init__(self, package=None): # default to package = None for bw compat |
| | | return _DottedNameResolver.__init__(self, package) |
| | | _DottedNameResolver.__init__(self, package) |
| | | |
| | | _marker = object() |
| | | |
| | |
| | | 'repoze.sphinx.autointerface', |
| | | 'pylons_sphinx_latesturl', |
| | | 'pylons-sphinx-themes', |
| | | 'sphinxcontrib-programoutput', |
| | | ] |
| | | |
| | | testing_extras = tests_require + [ |
| | |
| | | starter=pyramid.scaffolds:StarterProjectTemplate |
| | | zodb=pyramid.scaffolds:ZODBProjectTemplate |
| | | alchemy=pyramid.scaffolds:AlchemyProjectTemplate |
| | | [pyramid.pshell] |
| | | ipython=pyramid.scripts.pshell:PShellCommand.make_ipython_shell |
| | | bpython=pyramid.scripts.pshell:PShellCommand.make_bpython_shell |
| | | [pyramid.pshell_runner] |
| | | python=pyramid.scripts.pshell:python_shell_runner |
| | | [console_scripts] |
| | | pcreate = pyramid.scripts.pcreate:main |
| | | pserve = pyramid.scripts.pserve:main |
| | |
| | | [tox] |
| | | envlist = |
| | | py26,py27,py32,py33,py34,py35,pypy,pypy3,pep8, |
| | | {py2,py3}-docs, |
| | | py26,py27,py32,py33,py34,py35,pypy,pypy3, |
| | | docs,pep8, |
| | | {py2,py3}-cover,coverage, |
| | | |
| | | [testenv] |
| | |
| | | commands = |
| | | pip install pyramid[testing] |
| | | nosetests --with-xunit --xunit-file=nosetests-{envname}.xml {posargs:} |
| | | |
| | | # 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:py2-cover] |
| | | commands = |
| | | pip install pyramid[testing] |
| | | coverage run --source=pyramid {envbindir}/nosetests |
| | | coverage xml -o coverage-py2.xml |
| | | setenv = |
| | | COVERAGE_FILE=.coverage.py2 |
| | | |
| | | [testenv:py3-cover] |
| | | commands = |
| | | pip install pyramid[testing] |
| | | coverage run --source=pyramid {envbindir}/nosetests |
| | | coverage xml -o coverage-py3.xml |
| | | setenv = |
| | | COVERAGE_FILE=.coverage.py3 |
| | | |
| | | [testenv:coverage] |
| | | basepython = python3.4 |
| | | commands = |
| | | coverage erase |
| | | coverage combine |
| | | coverage xml |
| | | coverage report --show-missing --fail-under=100 |
| | | deps = |
| | | coverage |
| | | setenv = |
| | | COVERAGE_FILE=.coverage |
| | | |
| | | [testenv:py2-docs] |
| | | whitelist_externals = make |
| | | commands = |
| | | pip install pyramid[docs] |
| | | make -C docs html epub BUILDDIR={envdir} "SPHINXOPTS=-W -E" |
| | | |
| | | [testenv:py3-docs] |
| | | whitelist_externals = make |
| | | commands = |
| | | pip install pyramid[docs] |
| | | make -C docs html epub BUILDDIR={envdir} "SPHINXOPTS=-W -E" |
| | | |
| | | [testenv:py26-scaffolds] |
| | | basepython = python2.6 |
| | |
| | | deps = virtualenv |
| | | |
| | | [testenv:pep8] |
| | | basepython = python3.4 |
| | | basepython = python3.5 |
| | | commands = |
| | | flake8 pyramid/ |
| | | deps = |
| | | flake8 |
| | | |
| | | [testenv:docs] |
| | | basepython = python3.5 |
| | | whitelist_externals = make |
| | | commands = |
| | | pip install pyramid[docs] |
| | | make -C docs html epub BUILDDIR={envdir} "SPHINXOPTS=-W -E" |
| | | |
| | | # 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:py2-cover] |
| | | commands = |
| | | pip install pyramid[testing] |
| | | coverage run --source=pyramid {envbindir}/nosetests |
| | | coverage xml -o coverage-py2.xml |
| | | setenv = |
| | | COVERAGE_FILE=.coverage.py2 |
| | | |
| | | [testenv:py3-cover] |
| | | commands = |
| | | pip install pyramid[testing] |
| | | coverage run --source=pyramid {envbindir}/nosetests |
| | | coverage xml -o coverage-py3.xml |
| | | setenv = |
| | | COVERAGE_FILE=.coverage.py3 |
| | | |
| | | [testenv:coverage] |
| | | basepython = python3.5 |
| | | commands = |
| | | coverage erase |
| | | coverage combine |
| | | coverage xml |
| | | coverage report --show-missing --fail-under=100 |
| | | deps = |
| | | coverage |
| | | setenv = |
| | | COVERAGE_FILE=.coverage |