Merge branch 'master' into feature/alchemy-scaffold-update
Conflicts:
docs/tutorials/wiki2/basiclayout.rst
14 files deleted
12 files added
48 files modified
3 files renamed
| | |
| | | 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 |
| | | -------- |
| | | |
| | |
| | | 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. |
| | |
| | | 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 |
| | | |
| | | Deprecations |
| | | ------------ |
| | | |
| | |
| | | |
| | | - Donald Stufft, 2015/03/15 |
| | | |
| | | - Randy Topliffe, 2015/04/14 |
| | | |
| | | - Karen Dalton, 2015/06/01 |
| | | |
| | | - Igor Stroh, 2015/06/10 |
| | | |
| | | - Jesse Dhillon, 2015/10/07 |
| | | |
| | | - Amos Latteier, 2015/10/22 |
| | |
| | | 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 |
| | |
| | | |
| | | 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 |
| | | |
| | |
| | | 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/* |
| | | |
| | | |
| | | 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 |
| | | ================== |
| | |
| | | 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. |
| | |
| | | |
| | | .. index:: |
| | | single: interactive shell |
| | | single: IPython |
| | | single: pshell |
| | | single: bpython |
| | | |
| | | .. _interactive_shell: |
| | | |
| | |
| | | >>> request.route_url('home') |
| | | 'https://www.example.com/' |
| | | |
| | | .. index:: |
| | | single: IPython |
| | | single: bpython |
| | | |
| | | .. _ipython_or_bpython: |
| | | |
| | | IPython or bpython |
| | | ~~~~~~~~~~~~~~~~~~ |
| | | |
| | | 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. |
| | | |
| | | .. code-block:: text |
| | | |
| | | $ $VENV/bin/pshell -p ipython | bpython | python development.ini#MyProject |
| | | |
| | | Alternative Shells |
| | | ~~~~~~~~~~~~~~~~~~ |
| | | |
| | | 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 development.ini#MyProject |
| | | |
| | | 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 |
| | | |
| | |
| | | =============================== |
| | | |
| | | 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. |
| | |
| | | :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`` |
| | | <http://guide.python-distribute.org/creation.html>`_ 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. |
| | | |
| | |
| | | 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:: |
| | | |
| | |
| | | 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 |
| | | ``tests_require`` dependency on that package to your application's |
| | | ``setup.py`` file: |
| | | |
| | | .. literalinclude:: MyProject/setup.py |
| | |
| | | :emphasize-lines: 26-28,48 |
| | | :language: python |
| | | |
| | | Assuming your :term:`package` is named ``myproject``, which contains a |
| | | Let us assume 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: |
| | | |
| | |
| | | :linenos: |
| | | :language: python |
| | | |
| | | Then the following example functional test (shown below) demonstrates invoking |
| | | the :term:`view` shown above: |
| | | Then 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``. |
| | | |
| | | 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. |
| | |
| | | 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. |
| | | |
| | |
| | | language. SQLAlchemy uses "models" for this mapping. The scaffold |
| | | generated a sample model: |
| | | |
| | | .. literalinclude:: quick_tour/sqla_demo/sqla_demo/models/mymodel.py |
| | | .. literalinclude:: quick_tour/sqla_demo/sqla_demo/models.py |
| | | :start-after: Start Sphinx Include |
| | | :end-before: End Sphinx Include |
| | | |
| | | View code, which mediates the logic between web requests and the rest |
| | | of the system, can then easily get at the data thanks to SQLAlchemy: |
| | | |
| | | .. literalinclude:: quick_tour/sqla_demo/sqla_demo/views/default.py |
| | | .. literalinclude:: quick_tour/sqla_demo/sqla_demo/views.py |
| | | :start-after: Start Sphinx Include |
| | | :end-before: End Sphinx Include |
| | | |
| | |
| | | |
| | | [server:main] |
| | | use = egg:waitress#main |
| | | host = 127.0.0.1 |
| | | host = 0.0.0.0 |
| | | port = 6543 |
| | | |
| | | ### |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | |
| | | 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() |
| | | README = open(os.path.join(here, 'README.txt')).read() |
| | | CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() |
| | | |
| | | requires = [ |
| | | 'pyramid', |
| | | 'pyramid_jinja2', |
| | | 'pyramid_debugtoolbar', |
| | | 'pyramid_tm', |
| | | 'SQLAlchemy', |
| | | 'transaction', |
| | | 'pyramid_tm', |
| | | 'pyramid_debugtoolbar', |
| | | 'zope.sqlalchemy', |
| | | 'waitress', |
| | | ] |
| | |
| | | from pyramid.config import Configurator |
| | | from sqlalchemy import engine_from_config |
| | | |
| | | from .models import ( |
| | | DBSession, |
| | | Base, |
| | | ) |
| | | |
| | | |
| | | def main(global_config, **settings): |
| | | """ This function returns a Pyramid WSGI application. |
| | | """ |
| | | engine = engine_from_config(settings, 'sqlalchemy.') |
| | | DBSession.configure(bind=engine) |
| | | Base.metadata.bind = engine |
| | | config = Configurator(settings=settings) |
| | | config.include('pyramid_jinja2') |
| | | config.include('.models.meta') |
| | | config.add_static_view('static', 'static', cache_max_age=3600) |
| | | config.add_route('home', '/') |
| | | config.scan() |
New file |
| | |
| | | from sqlalchemy import ( |
| | | Column, |
| | | Integer, |
| | | Text, |
| | | ) |
| | | |
| | | from sqlalchemy.ext.declarative import declarative_base |
| | | |
| | | from sqlalchemy.orm import ( |
| | | scoped_session, |
| | | sessionmaker, |
| | | ) |
| | | |
| | | from zope.sqlalchemy import ZopeTransactionExtension |
| | | |
| | | DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) |
| | | Base = declarative_base() |
| | | |
| | | # Start Sphinx Include |
| | | class MyModel(Base): |
| | | __tablename__ = 'models' |
| | | id = Column(Integer, primary_key=True) |
| | | name = Column(Text, unique=True) |
| | | value = Column(Integer) |
| | | |
| | | def __init__(self, name, value): |
| | | self.name = name |
| | | self.value = value |
| | | # End Sphinx Include |
| | |
| | | import sys |
| | | import transaction |
| | | |
| | | from sqlalchemy import engine_from_config |
| | | |
| | | from pyramid.paster import ( |
| | | get_appsettings, |
| | | setup_logging, |
| | | ) |
| | | |
| | | from pyramid.scripts.common import parse_vars |
| | | |
| | | from ..models.meta import ( |
| | | from ..models import ( |
| | | DBSession, |
| | | MyModel, |
| | | Base, |
| | | get_session, |
| | | get_engine, |
| | | get_dbmaker, |
| | | ) |
| | | from ..models.mymodel import MyModel |
| | | |
| | | |
| | | def usage(argv): |
| | | cmd = os.path.basename(argv[0]) |
| | | print('usage: %s <config_uri> [var=value]\n' |
| | | print('usage: %s <config_uri>\n' |
| | | '(example: "%s development.ini")' % (cmd, cmd)) |
| | | sys.exit(1) |
| | | |
| | | |
| | | def main(argv=sys.argv): |
| | | if len(argv) < 2: |
| | | if len(argv) != 2: |
| | | usage(argv) |
| | | config_uri = argv[1] |
| | | options = parse_vars(argv[2:]) |
| | | setup_logging(config_uri) |
| | | settings = get_appsettings(config_uri, options=options) |
| | | |
| | | engine = get_engine(settings) |
| | | dbmaker = get_dbmaker(engine) |
| | | |
| | | dbsession = get_session(transaction.manager, dbmaker) |
| | | |
| | | settings = get_appsettings(config_uri) |
| | | engine = engine_from_config(settings, 'sqlalchemy.') |
| | | DBSession.configure(bind=engine) |
| | | Base.metadata.create_all(engine) |
| | | |
| | | with transaction.manager: |
| | | model = MyModel(name='one', value=1) |
| | | dbsession.add(model) |
| | | DBSession.add(model) |
New file |
| | |
| | | * html img, |
| | | * html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none", |
| | | this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')", |
| | | this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''), |
| | | this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')", |
| | | this.runtimeStyle.backgroundImage = "none")),this.pngSet=true) |
| | | );} |
| | | #wrap{display:table;height:100%} |
New file |
| | |
| | | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td |
| | | { |
| | | margin: 0; |
| | | padding: 0; |
| | | border: 0; |
| | | outline: 0; |
| | | font-size: 100%; /* 16px */ |
| | | vertical-align: baseline; |
| | | background: transparent; |
| | | } |
| | | |
| | | body |
| | | { |
| | | line-height: 1; |
| | | } |
| | | |
| | | ol, ul |
| | | { |
| | | list-style: none; |
| | | } |
| | | |
| | | blockquote, q |
| | | { |
| | | quotes: none; |
| | | } |
| | | |
| | | blockquote:before, blockquote:after, q:before, q:after |
| | | { |
| | | content: ''; |
| | | content: none; |
| | | } |
| | | |
| | | :focus |
| | | { |
| | | outline: 0; |
| | | } |
| | | |
| | | ins |
| | | { |
| | | text-decoration: none; |
| | | } |
| | | |
| | | del |
| | | { |
| | | text-decoration: line-through; |
| | | } |
| | | |
| | | table |
| | | { |
| | | border-collapse: collapse; |
| | | border-spacing: 0; |
| | | } |
| | | |
| | | sub |
| | | { |
| | | vertical-align: sub; |
| | | font-size: smaller; |
| | | line-height: normal; |
| | | } |
| | | |
| | | sup |
| | | { |
| | | vertical-align: super; |
| | | font-size: smaller; |
| | | line-height: normal; |
| | | } |
| | | |
| | | ul, menu, dir |
| | | { |
| | | display: block; |
| | | list-style-type: disc; |
| | | margin: 1em 0; |
| | | padding-left: 40px; |
| | | } |
| | | |
| | | ol |
| | | { |
| | | display: block; |
| | | list-style-type: decimal-leading-zero; |
| | | margin: 1em 0; |
| | | padding-left: 40px; |
| | | } |
| | | |
| | | li |
| | | { |
| | | display: list-item; |
| | | } |
| | | |
| | | ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl |
| | | { |
| | | margin-top: 0; |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir |
| | | { |
| | | list-style-type: circle; |
| | | } |
| | | |
| | | ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir |
| | | { |
| | | list-style-type: square; |
| | | } |
| | | |
| | | .hidden |
| | | { |
| | | display: none; |
| | | } |
| | | |
| | | p |
| | | { |
| | | line-height: 1.5em; |
| | | } |
| | | |
| | | h1 |
| | | { |
| | | font-size: 1.75em; |
| | | line-height: 1.7em; |
| | | font-family: helvetica, verdana; |
| | | } |
| | | |
| | | h2 |
| | | { |
| | | font-size: 1.5em; |
| | | line-height: 1.7em; |
| | | font-family: helvetica, verdana; |
| | | } |
| | | |
| | | h3 |
| | | { |
| | | font-size: 1.25em; |
| | | line-height: 1.7em; |
| | | font-family: helvetica, verdana; |
| | | } |
| | | |
| | | h4 |
| | | { |
| | | font-size: 1em; |
| | | line-height: 1.7em; |
| | | font-family: helvetica, verdana; |
| | | } |
| | | |
| | | html, body |
| | | { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | |
| | | body |
| | | { |
| | | margin: 0; |
| | | padding: 0; |
| | | background-color: #fff; |
| | | position: relative; |
| | | font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif; |
| | | } |
| | | |
| | | a |
| | | { |
| | | color: #1b61d6; |
| | | text-decoration: none; |
| | | } |
| | | |
| | | a:hover |
| | | { |
| | | color: #e88f00; |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | body h1, body h2, body h3, body h4, body h5, body h6 |
| | | { |
| | | font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif; |
| | | font-weight: 400; |
| | | color: #373839; |
| | | font-style: normal; |
| | | } |
| | | |
| | | #wrap |
| | | { |
| | | min-height: 100%; |
| | | } |
| | | |
| | | #header, #footer |
| | | { |
| | | width: 100%; |
| | | color: #fff; |
| | | height: 40px; |
| | | position: absolute; |
| | | text-align: center; |
| | | line-height: 40px; |
| | | overflow: hidden; |
| | | font-size: 12px; |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | #header |
| | | { |
| | | background: #000; |
| | | top: 0; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | #footer |
| | | { |
| | | bottom: 0; |
| | | background: #000 url(footerbg.png) repeat-x 0 top; |
| | | position: relative; |
| | | margin-top: -40px; |
| | | clear: both; |
| | | } |
| | | |
| | | .header, .footer |
| | | { |
| | | width: 750px; |
| | | margin-right: auto; |
| | | margin-left: auto; |
| | | } |
| | | |
| | | .wrapper |
| | | { |
| | | width: 100%; |
| | | } |
| | | |
| | | #top, #top-small, #bottom |
| | | { |
| | | width: 100%; |
| | | } |
| | | |
| | | #top |
| | | { |
| | | color: #000; |
| | | height: 230px; |
| | | background: #fff url(headerbg.png) repeat-x 0 top; |
| | | position: relative; |
| | | } |
| | | |
| | | #top-small |
| | | { |
| | | color: #000; |
| | | height: 60px; |
| | | background: #fff url(headerbg.png) repeat-x 0 top; |
| | | position: relative; |
| | | } |
| | | |
| | | #bottom |
| | | { |
| | | color: #222; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | .top, .top-small, .middle, .bottom |
| | | { |
| | | width: 750px; |
| | | margin-right: auto; |
| | | margin-left: auto; |
| | | } |
| | | |
| | | .top |
| | | { |
| | | padding-top: 40px; |
| | | } |
| | | |
| | | .top-small |
| | | { |
| | | padding-top: 10px; |
| | | } |
| | | |
| | | #middle |
| | | { |
| | | width: 100%; |
| | | height: 100px; |
| | | background: url(middlebg.png) repeat-x; |
| | | border-top: 2px solid #fff; |
| | | border-bottom: 2px solid #b2b2b2; |
| | | } |
| | | |
| | | .app-welcome |
| | | { |
| | | margin-top: 25px; |
| | | } |
| | | |
| | | .app-name |
| | | { |
| | | color: #000; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .bottom |
| | | { |
| | | padding-top: 50px; |
| | | } |
| | | |
| | | #left |
| | | { |
| | | width: 350px; |
| | | float: left; |
| | | padding-right: 25px; |
| | | } |
| | | |
| | | #right |
| | | { |
| | | width: 350px; |
| | | float: right; |
| | | padding-left: 25px; |
| | | } |
| | | |
| | | .align-left |
| | | { |
| | | text-align: left; |
| | | } |
| | | |
| | | .align-right |
| | | { |
| | | text-align: right; |
| | | } |
| | | |
| | | .align-center |
| | | { |
| | | text-align: center; |
| | | } |
| | | |
| | | ul.links |
| | | { |
| | | margin: 0; |
| | | padding: 0; |
| | | } |
| | | |
| | | ul.links li |
| | | { |
| | | list-style-type: none; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | form |
| | | { |
| | | border-style: none; |
| | | } |
| | | |
| | | fieldset |
| | | { |
| | | border-style: none; |
| | | } |
| | | |
| | | input |
| | | { |
| | | color: #222; |
| | | border: 1px solid #ccc; |
| | | font-family: sans-serif; |
| | | font-size: 12px; |
| | | line-height: 16px; |
| | | } |
| | | |
| | | input[type=text], input[type=password] |
| | | { |
| | | width: 205px; |
| | | } |
| | | |
| | | input[type=submit] |
| | | { |
| | | background-color: #ddd; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | /*Opera Fix*/ |
| | | body:before |
| | | { |
| | | content: ""; |
| | | height: 100%; |
| | | float: left; |
| | | width: 0; |
| | | margin-top: -32767px; |
| | | } |
New file |
| | |
| | | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
| | | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> |
| | | <head> |
| | | <title>The Pyramid Web Framework</title> |
| | | <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> |
| | | <meta name="keywords" content="python web application" /> |
| | | <meta name="description" content="pyramid web application" /> |
| | | <link rel="shortcut icon" href="${request.static_url('sqla_demo:static/favicon.ico')}" /> |
| | | <link rel="stylesheet" href="${request.static_url('sqla_demo:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" /> |
| | | <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" /> |
| | | <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" /> |
| | | <!--[if lte IE 6]> |
| | | <link rel="stylesheet" href="${request.static_url('sqla_demo:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" /> |
| | | <![endif]--> |
| | | </head> |
| | | <body> |
| | | <div id="wrap"> |
| | | <div id="top"> |
| | | <div class="top align-center"> |
| | | <div><img src="${request.static_url('sqla_demo:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div> |
| | | </div> |
| | | </div> |
| | | <div id="middle"> |
| | | <div class="middle align-center"> |
| | | <p class="app-welcome"> |
| | | Welcome to <span class="app-name">${project}</span>, an application generated by<br/> |
| | | the Pyramid web framework. |
| | | </p> |
| | | </div> |
| | | </div> |
| | | <div id="bottom"> |
| | | <div class="bottom"> |
| | | <div id="left" class="align-right"> |
| | | <h2>Search documentation</h2> |
| | | <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/search.html"> |
| | | <input type="text" id="q" name="q" value="" /> |
| | | <input type="submit" id="x" value="Go" /> |
| | | </form> |
| | | </div> |
| | | <div id="right" class="align-left"> |
| | | <h2>Pyramid links</h2> |
| | | <ul class="links"> |
| | | <li> |
| | | <a href="http://pylonsproject.org">Pylons Website</a> |
| | | </li> |
| | | <li> |
| | | <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#narrative-documentation">Narrative Documentation</a> |
| | | </li> |
| | | <li> |
| | | <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#reference-material">API Documentation</a> |
| | | </li> |
| | | <li> |
| | | <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#tutorials">Tutorials</a> |
| | | </li> |
| | | <li> |
| | | <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#detailed-change-history">Change History</a> |
| | | </li> |
| | | <li> |
| | | <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#sample-applications">Sample Applications</a> |
| | | </li> |
| | | <li> |
| | | <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#support-and-development">Support and Development</a> |
| | | </li> |
| | | <li> |
| | | <a href="irc://irc.freenode.net#pyramid">IRC Channel</a> |
| | | </li> |
| | | </ul> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div id="footer"> |
| | | <div class="footer">© Copyright 2008-2012, Agendaless Consulting.</div> |
| | | </div> |
| | | </body> |
| | | </html> |
| | |
| | | |
| | | from pyramid import testing |
| | | |
| | | |
| | | def dummy_request(dbsession): |
| | | return testing.DummyRequest(dbsession=dbsession) |
| | | from .models import DBSession |
| | | |
| | | |
| | | class BaseTest(unittest.TestCase): |
| | | class TestMyView(unittest.TestCase): |
| | | def setUp(self): |
| | | self.config = testing.setUp(settings={ |
| | | 'sqlalchemy.url': 'sqlite:///:memory:' |
| | | }) |
| | | self.config.include('.models.meta') |
| | | settings = self.config.get_settings() |
| | | |
| | | from .models.meta import ( |
| | | get_session, |
| | | get_engine, |
| | | get_dbmaker, |
| | | self.config = testing.setUp() |
| | | from sqlalchemy import create_engine |
| | | engine = create_engine('sqlite://') |
| | | from .models import ( |
| | | Base, |
| | | MyModel, |
| | | ) |
| | | |
| | | self.engine = get_engine(settings) |
| | | dbmaker = get_dbmaker(self.engine) |
| | | |
| | | self.session = get_session(transaction.manager, dbmaker) |
| | | |
| | | def init_database(self): |
| | | from .models.meta import Base |
| | | Base.metadata.create_all(self.engine) |
| | | DBSession.configure(bind=engine) |
| | | Base.metadata.create_all(engine) |
| | | with transaction.manager: |
| | | model = MyModel(name='one', value=55) |
| | | DBSession.add(model) |
| | | |
| | | def tearDown(self): |
| | | from .models.meta import Base |
| | | |
| | | DBSession.remove() |
| | | testing.tearDown() |
| | | transaction.abort() |
| | | Base.metadata.create_all(self.engine) |
| | | |
| | | |
| | | class TestMyViewSuccessCondition(BaseTest): |
| | | |
| | | def setUp(self): |
| | | super(TestMyViewSuccessCondition, self).setUp() |
| | | self.init_database() |
| | | |
| | | from .models.mymodel import MyModel |
| | | |
| | | model = MyModel(name='one', value=55) |
| | | self.session.add(model) |
| | | |
| | | def test_passing_view(self): |
| | | from .views.default import my_view |
| | | info = my_view(dummy_request(self.session)) |
| | | def test_it(self): |
| | | from .views import my_view |
| | | request = testing.DummyRequest() |
| | | info = my_view(request) |
| | | self.assertEqual(info['one'].name, 'one') |
| | | self.assertEqual(info['project'], 'sqla_demo') |
| | | |
| | | |
| | | class TestMyViewFailureCondition(BaseTest): |
| | | |
| | | def test_failing_view(self): |
| | | from .views.default import my_view |
| | | info = my_view(dummy_request(self.session)) |
| | | self.assertEqual(info.status_int, 500) |
File was renamed from docs/quick_tour/sqla_demo/sqla_demo/views/default.py |
| | |
| | | |
| | | from sqlalchemy.exc import DBAPIError |
| | | |
| | | from ..models.mymodel import MyModel |
| | | from .models import ( |
| | | DBSession, |
| | | MyModel, |
| | | ) |
| | | |
| | | |
| | | @view_config(route_name='home', renderer='../templates/mytemplate.jinja2') |
| | | @view_config(route_name='home', renderer='templates/mytemplate.pt') |
| | | def my_view(request): |
| | | try: |
| | | query = request.dbsession.query(MyModel) |
| | | # Start Sphinx Include |
| | | one = query.filter(MyModel.name == 'one').first() |
| | | one = DBSession.query(MyModel).filter(MyModel.name == 'one').first() |
| | | # End Sphinx Include |
| | | except DBAPIError: |
| | | return Response(db_err_msg, content_type='text/plain', status_int=500) |
| | | return Response(conn_err_msg, content_type='text/plain', status_int=500) |
| | | return {'one': one, 'project': 'sqla_demo'} |
| | | |
| | | |
| | | db_err_msg = """\ |
| | | conn_err_msg = """\ |
| | | Pyramid is having a problem using your SQL database. The problem |
| | | might be caused by one of the following things: |
| | | |
| | | 1. You may need to run the "initialize_sqla_demo_db" script |
| | | to initialize your database tables. Check your virtual |
| | | to initialize your database tables. Check your virtual |
| | | environment's "bin" directory for this script and try to run it. |
| | | |
| | | 2. Your database server may not be running. Check that the |
| | |
| | | After you fix the problem, please restart the Pyramid application to |
| | | try it again. |
| | | """ |
| | | |
| | |
| | | |
| | | A directory on disk can be turned into a Python :term:`package` by containing |
| | | an ``__init__.py`` file. Even if empty, this marks a directory as a Python |
| | | package. We use ``__init__.py`` both as a marker, indicating the directory in |
| | | which it's contained is a package, and to contain application configuration |
| | | package. We use ``__init__.py`` both as a marker, indicating the directory |
| | | in which it's contained is a package, and to contain application configuration |
| | | code. |
| | | |
| | | Open ``tutorial/tutorial/__init__.py``. It should already contain the |
| | | following: |
| | | Open ``tutorial/tutorial/__init__.py``. It should already contain |
| | | the following: |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/__init__.py |
| | | :linenos: |
| | |
| | | above is executed. It accepts some settings and returns a :term:`WSGI` |
| | | application. (See :ref:`startup_chapter` for more about ``pserve``.) |
| | | |
| | | The main function first creates a :term:`SQLAlchemy` database engine using |
| | | :func:`sqlalchemy.engine_from_config` from the ``sqlalchemy.`` prefixed |
| | | settings in the ``development.ini`` file's ``[app:main]`` section. |
| | | This will be a URI (something like ``sqlite://``): |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/__init__.py |
| | | :lines: 13 |
| | | :language: py |
| | | |
| | | ``main`` then initializes our SQLAlchemy session object, passing it the |
| | | engine: |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/__init__.py |
| | | :lines: 14 |
| | | :language: py |
| | | |
| | | ``main`` subsequently initializes our SQLAlchemy declarative ``Base`` object, |
| | | assigning the engine we created to the ``bind`` attribute of it's |
| | | ``metadata`` object. This allows table definitions done imperatively |
| | | (instead of declaratively, via a class statement) to work. We won't use any |
| | | such tables in our application, but if you add one later, long after you've |
| | | forgotten about this tutorial, you won't be left scratching your head when it |
| | | doesn't work. |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/__init__.py |
| | | :lines: 15 |
| | | :language: py |
| | | |
| | | The next step of ``main`` is to construct a :term:`Configurator` object: |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/__init__.py |
| | | :lines: 7 |
| | | :lines: 16 |
| | | :language: py |
| | | |
| | | ``settings`` is passed to the Configurator as a keyword argument with the |
| | | dictionary values passed as the ``**settings`` argument. This will be a |
| | | dictionary of settings parsed from the ``.ini`` file, which contains |
| | | deployment-related values such as ``pyramid.reload_templates``, |
| | | ``sqlalchemy.url``, and so on. |
| | | ``db_string``, etc. |
| | | |
| | | Next include :term:`Jinja2` templating bindings so that we can use renderers |
| | | with the ``.jinja2`` extension within our project. |
| | | Next, include :term:`Chameleon` templating bindings so that we can use |
| | | renderers with the ``.pt`` extension within our project. |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/__init__.py |
| | | :lines: 8 |
| | | :language: py |
| | | |
| | | Next include the module ``meta`` from the package ``models`` using a dotted |
| | | Python path. |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/__init__.py |
| | | :lines: 9 |
| | | :lines: 17 |
| | | :language: py |
| | | |
| | | ``main`` now calls :meth:`pyramid.config.Configurator.add_static_view` with |
| | | two arguments: ``static`` (the name), and ``static`` (the path): |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/__init__.py |
| | | :lines: 10 |
| | | :lines: 18 |
| | | :language: py |
| | | |
| | | This registers a static resource view which will match any URL that starts |
| | |
| | | used when the URL is ``/``: |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/__init__.py |
| | | :lines: 11 |
| | | :lines: 19 |
| | | :language: py |
| | | |
| | | Since this route has a ``pattern`` equaling ``/``, it is the route that will |
| | | be matched when the URL ``/`` is visited, e.g., ``http://localhost:6543/``. |
| | | Since this route has a ``pattern`` equaling ``/`` it is the route that will |
| | | be matched when the URL ``/`` is visited, e.g. ``http://localhost:6543/``. |
| | | |
| | | ``main`` next calls the ``scan`` method of the configurator |
| | | (:meth:`pyramid.config.Configurator.scan`), which will recursively scan our |
| | |
| | | application URLs to be mapped to some code. |
| | | |
| | | .. literalinclude:: src/basiclayout/tutorial/__init__.py |
| | | :lines: 12 |
| | | :lines: 20 |
| | | :language: py |
| | | |
| | | Finally ``main`` is finished configuring things, so it uses the |
| | | Finally, ``main`` is finished configuring things, so it uses the |
| | | :meth:`pyramid.config.Configurator.make_wsgi_app` method to return a |
| | | :term:`WSGI` application: |
| | | |
| | |
| | | |
| | | [server:main] |
| | | use = egg:waitress#main |
| | | host = 127.0.0.1 |
| | | host = 0.0.0.0 |
| | | port = 6543 |
| | | |
| | | ### |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | |
| | | formatter = generic |
| | | |
| | | [formatter_generic] |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s |
| | | format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s |
| | |
| | | from pyramid.config import Configurator |
| | | from sqlalchemy import engine_from_config |
| | | |
| | | from .models import ( |
| | | DBSession, |
| | | Base, |
| | | ) |
| | | |
| | | |
| | | def main(global_config, **settings): |
| | | """ This function returns a Pyramid WSGI application. |
| | | """ |
| | | engine = engine_from_config(settings, 'sqlalchemy.') |
| | | DBSession.configure(bind=engine) |
| | | Base.metadata.bind = engine |
| | | config = Configurator(settings=settings) |
| | | config.include('pyramid_jinja2') |
| | | config.include('.models.meta') |
| | | config.include('pyramid_chameleon') |
| | | config.add_static_view('static', 'static', cache_max_age=3600) |
| | | config.add_route('home', '/') |
| | | config.scan() |
| | |
| | | |
| | | 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. |
| | |
| | | from pyramid.config import Configurator |
| | | from sqlalchemy import engine_from_config |
| | | |
| | | from .models import ( |
| | | DBSession, |
| | | Base, |
| | | ) |
| | | |
| | | |
| | | def main(global_config, **settings): |
| | | """ This function returns a Pyramid WSGI application. |
| | | """ |
| | | engine = engine_from_config(settings, 'sqlalchemy.') |
| | | DBSession.configure(bind=engine) |
| | | Base.metadata.bind = engine |
| | | config = Configurator(settings=settings) |
| | | config.include('pyramid_jinja2') |
| | | config.include('.models.meta') |
| | | config.include('pyramid_chameleon') |
| | | config.add_static_view('static', 'static', cache_max_age=3600) |
| | | config.add_route('home', '/') |
| | | config.scan() |
New file |
| | |
| | | from sqlalchemy import ( |
| | | Column, |
| | | Index, |
| | | Integer, |
| | | Text, |
| | | ) |
| | | |
| | | from sqlalchemy.ext.declarative import declarative_base |
| | | |
| | | from sqlalchemy.orm import ( |
| | | scoped_session, |
| | | sessionmaker, |
| | | ) |
| | | |
| | | from zope.sqlalchemy import ZopeTransactionExtension |
| | | |
| | | DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) |
| | | Base = declarative_base() |
| | | |
| | | |
| | | class MyModel(Base): |
| | | __tablename__ = 'models' |
| | | id = Column(Integer, primary_key=True) |
| | | name = Column(Text) |
| | | value = Column(Integer) |
| | | |
| | | Index('my_index', MyModel.name, unique=True, mysql_length=255) |
| | |
| | | import sys |
| | | import transaction |
| | | |
| | | from sqlalchemy import engine_from_config |
| | | |
| | | from pyramid.paster import ( |
| | | get_appsettings, |
| | | setup_logging, |
| | |
| | | |
| | | from pyramid.scripts.common import parse_vars |
| | | |
| | | from ..models.meta import ( |
| | | from ..models import ( |
| | | DBSession, |
| | | MyModel, |
| | | Base, |
| | | get_session, |
| | | get_engine, |
| | | get_dbmaker, |
| | | ) |
| | | from ..models.mymodel import MyModel |
| | | |
| | | |
| | | def usage(argv): |
| | |
| | | options = parse_vars(argv[2:]) |
| | | setup_logging(config_uri) |
| | | settings = get_appsettings(config_uri, options=options) |
| | | |
| | | engine = get_engine(settings) |
| | | dbmaker = get_dbmaker(engine) |
| | | |
| | | dbsession = get_session(transaction.manager, dbmaker) |
| | | |
| | | engine = engine_from_config(settings, 'sqlalchemy.') |
| | | DBSession.configure(bind=engine) |
| | | Base.metadata.create_all(engine) |
| | | |
| | | with transaction.manager: |
| | | model = MyModel(name='one', value=1) |
| | | dbsession.add(model) |
| | | DBSession.add(model) |
File was renamed from pyramid/scaffolds/alchemy/+package+/templates/layout.jinja2_tmpl |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="\{\{request.locale_name\}\}"> |
| | | <html lang="${request.locale_name}"> |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | | <meta name="description" content="pyramid web application"> |
| | | <meta name="author" content="Pylons Project"> |
| | | <link rel="shortcut icon" href="\{\{request.static_url('{{package}}:static/pyramid-16x16.png')\}\}"> |
| | | <link rel="shortcut icon" href="${request.static_url('{{package}}:static/pyramid-16x16.png')}"> |
| | | |
| | | <title>Alchemy Scaffold for The Pyramid Web Framework</title> |
| | | |
| | |
| | | <link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet"> |
| | | |
| | | <!-- Custom styles for this scaffold --> |
| | | <link href="\{\{request.static_url('{{package}}:static/theme.css')\}\}" rel="stylesheet"> |
| | | <link href="${request.static_url('{{package}}:static/theme.css')}" rel="stylesheet"> |
| | | |
| | | <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --> |
| | | <!--[if lt IE 9]> |
| | |
| | | <div class="container"> |
| | | <div class="row"> |
| | | <div class="col-md-2"> |
| | | <img class="logo img-responsive" src="\{\{request.static_url('{{package}}:static/pyramid.png')\}\}" alt="pyramid web framework"> |
| | | <img class="logo img-responsive" src="${request.static_url('{{package}}:static/pyramid.png')}" alt="pyramid web framework"> |
| | | </div> |
| | | <div class="col-md-10"> |
| | | {% block content %} |
| | | <p>No content</p> |
| | | {% endblock content %} |
| | | <div class="content"> |
| | | <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy 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 {{pyramid_version}}</span>.</p> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="row"> |
| | |
| | | |
| | | from pyramid import testing |
| | | |
| | | |
| | | def dummy_request(dbsession): |
| | | return testing.DummyRequest(dbsession=dbsession) |
| | | from .models import DBSession |
| | | |
| | | |
| | | class BaseTest(unittest.TestCase): |
| | | class TestMyViewSuccessCondition(unittest.TestCase): |
| | | def setUp(self): |
| | | self.config = testing.setUp(settings={ |
| | | 'sqlalchemy.url': 'sqlite:///:memory:' |
| | | }) |
| | | self.config.include('.models.meta') |
| | | settings = self.config.get_settings() |
| | | |
| | | from .models.meta import ( |
| | | get_session, |
| | | get_engine, |
| | | get_dbmaker, |
| | | self.config = testing.setUp() |
| | | from sqlalchemy import create_engine |
| | | engine = create_engine('sqlite://') |
| | | from .models import ( |
| | | Base, |
| | | MyModel, |
| | | ) |
| | | |
| | | self.engine = get_engine(settings) |
| | | dbmaker = get_dbmaker(self.engine) |
| | | |
| | | self.session = get_session(transaction.manager, dbmaker) |
| | | |
| | | def init_database(self): |
| | | from .models.meta import Base |
| | | Base.metadata.create_all(self.engine) |
| | | DBSession.configure(bind=engine) |
| | | Base.metadata.create_all(engine) |
| | | with transaction.manager: |
| | | model = MyModel(name='one', value=55) |
| | | DBSession.add(model) |
| | | |
| | | def tearDown(self): |
| | | from .models.meta import Base |
| | | |
| | | DBSession.remove() |
| | | testing.tearDown() |
| | | transaction.abort() |
| | | Base.metadata.create_all(self.engine) |
| | | |
| | | |
| | | class TestMyViewSuccessCondition(BaseTest): |
| | | |
| | | def setUp(self): |
| | | super(TestMyViewSuccessCondition, self).setUp() |
| | | self.init_database() |
| | | |
| | | from .models.mymodel import MyModel |
| | | |
| | | model = MyModel(name='one', value=55) |
| | | self.session.add(model) |
| | | |
| | | def test_passing_view(self): |
| | | from .views.default import my_view |
| | | info = my_view(dummy_request(self.session)) |
| | | from .views import my_view |
| | | request = testing.DummyRequest() |
| | | info = my_view(request) |
| | | self.assertEqual(info['one'].name, 'one') |
| | | self.assertEqual(info['project'], '{{project}}') |
| | | |
| | | |
| | | class TestMyViewFailureCondition(BaseTest): |
| | | class TestMyViewFailureCondition(unittest.TestCase): |
| | | def setUp(self): |
| | | self.config = testing.setUp() |
| | | from sqlalchemy import create_engine |
| | | engine = create_engine('sqlite://') |
| | | from .models import ( |
| | | Base, |
| | | MyModel, |
| | | ) |
| | | DBSession.configure(bind=engine) |
| | | |
| | | def tearDown(self): |
| | | DBSession.remove() |
| | | testing.tearDown() |
| | | |
| | | def test_failing_view(self): |
| | | from .views.default import my_view |
| | | info = my_view(dummy_request(self.session)) |
| | | self.assertEqual(info.status_int, 500) |
| | | from .views import my_view |
| | | request = testing.DummyRequest() |
| | | info = my_view(request) |
| | | self.assertEqual(info.status_int, 500) |
File was renamed from pyramid/scaffolds/alchemy/+package+/views/default.py_tmpl |
| | |
| | | |
| | | from sqlalchemy.exc import DBAPIError |
| | | |
| | | from ..models.mymodel import MyModel |
| | | from .models import ( |
| | | DBSession, |
| | | MyModel, |
| | | ) |
| | | |
| | | |
| | | @view_config(route_name='home', renderer='../templates/mytemplate.jinja2') |
| | | @view_config(route_name='home', renderer='templates/mytemplate.pt') |
| | | def my_view(request): |
| | | try: |
| | | query = request.dbsession.query(MyModel) |
| | | one = query.filter(MyModel.name == 'one').first() |
| | | one = DBSession.query(MyModel).filter(MyModel.name == 'one').first() |
| | | except DBAPIError: |
| | | return Response(db_err_msg, content_type='text/plain', status_int=500) |
| | | return Response(conn_err_msg, content_type='text/plain', status_int=500) |
| | | return {'one': one, 'project': '{{project}}'} |
| | | |
| | | |
| | | db_err_msg = """\ |
| | | conn_err_msg = """\ |
| | | Pyramid is having a problem using your SQL database. The problem |
| | | might be caused by one of the following things: |
| | | |
| | |
| | | After you fix the problem, please restart the Pyramid application to |
| | | try it again. |
| | | """ |
| | | |
| | |
| | | |
| | | requires = [ |
| | | 'pyramid', |
| | | 'pyramid_jinja2', |
| | | 'pyramid_chameleon', |
| | | 'pyramid_debugtoolbar', |
| | | 'pyramid_tm', |
| | | 'SQLAlchemy', |
| | |
| | | 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): |
| | | """ |
| | |
| | | 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: |
| | |
| | | |
| | | 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 |
| | | |
| | | |
| | |
| | | 'cd7a2fa4910000000auserid!') |
| | | result = self._callFUT('secret', ticket, '2001:db8::1', 'sha256') |
| | | self.assertEqual(result, (10, 'userid', [''], '')) |
| | | pass |
| | | |
| | | class TestSessionAuthenticationPolicy(unittest.TestCase): |
| | | def _getTargetClass(self): |
| | |
| | | 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) |
| | | |
| | |
| | | |
| | | 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() |
| | | |
| | |
| | | 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 |