Merge branch 'master' of github.com:Pylons/pyramid
8 files added
79 files modified
| | |
| | | env: TOXENV=py35 |
| | | - python: pypy |
| | | env: TOXENV=pypy |
| | | - python: pypy3 |
| | | env: TOXENV=pypy3 |
| | | - python: 3.5 |
| | | env: TOXENV=py2-cover,py3-cover,coverage |
| | | - python: 3.5 |
| | |
| | | Backward Incompatibilities |
| | | -------------------------- |
| | | |
| | | - Following the Pyramid deprecation period (1.4 -> 1.6), |
| | | AuthTktAuthenticationPolicy's default hashing algorithm is changing from md5 |
| | | to sha512. If you are using the authentication policy and need to continue |
| | | using md5, please explicitly set hashalg to 'md5'. |
| | | |
| | | This change does mean that any existing auth tickets (and associated cookies) |
| | | will no longer be valid, and users will no longer be logged in, and have to |
| | | login to their accounts again. |
| | | |
| | | See https://github.com/Pylons/pyramid/pull/2496 |
| | | |
| | | - The ``check_csrf_token`` function no longer validates a csrf token in the |
| | | query string of a request. Only headers and request bodies are supported. |
| | | See https://github.com/Pylons/pyramid/pull/2500 |
| | | |
| | | Features |
| | | -------- |
| | | |
| | | - Added a new setting, ``pyramid.require_default_csrf`` which may be used |
| | | to turn on CSRF checks globally for every POST request in the application. |
| | | This should be considered a good default for websites built on Pyramid. |
| | | It is possible to opt-out of CSRF checks on a per-view basis by setting |
| | | ``require_csrf=False`` on those views. |
| | | See https://github.com/Pylons/pyramid/pull/2413 |
| | | |
| | | - Added a ``require_csrf`` view option which will enforce CSRF checks on any |
| | | request with an unsafe method as defined by RFC2616. If the CSRF check fails |
| | | a ``BadCSRFToken`` exception will be raised and may be caught by exception |
| | | views (the default response is a ``400 Bad Request``). This option should be |
| | | used in place of the deprecated ``check_csrf`` view predicate which would |
| | | normally result in unexpected ``404 Not Found`` response to the client |
| | | instead of a catchable exception. See |
| | | https://github.com/Pylons/pyramid/pull/2413 and |
| | | https://github.com/Pylons/pyramid/pull/2500 |
| | | |
| | | - Added an additional CSRF validation that checks the origin/referrer of a |
| | | request and makes sure it matches the current ``request.domain``. This |
| | | particular check is only active when accessing a site over HTTPS as otherwise |
| | | browsers don't always send the required information. If this additional CSRF |
| | | validation fails a ``BadCSRFOrigin`` exception will be raised and may be |
| | | caught by exception views (the default response is ``400 Bad Request``). |
| | | Additional allowed origins may be configured by setting |
| | | ``pyramid.csrf_trusted_origins`` to a list of domain names (with ports if on |
| | | a non standard port) to allow. Subdomains are not allowed unless the domain |
| | | name has been prefixed with a ``.``. See |
| | | https://github.com/Pylons/pyramid/pull/2501 |
| | | |
| | | - Added a new ``pyramid.session.check_csrf_origin`` API for validating the |
| | | origin or referrer headers against the request's domain. |
| | | See https://github.com/Pylons/pyramid/pull/2501 |
| | | |
| | | - Pyramid HTTPExceptions will now take into account the best match for the |
| | | clients Accept header, and depending on what is requested will return |
| | | text/html, application/json or text/plain. The default for */* is still |
| | | text/html, but if application/json is explicitly mentioned it will now |
| | | receive a valid JSON response. See |
| | | https://github.com/Pylons/pyramid/pull/2489 |
| | | |
| | | - A new event and interface (BeforeTraversal) has been introduced that will |
| | | notify listeners before traversal starts in the router. See |
| | | https://github.com/Pylons/pyramid/pull/2469 and |
| | | https://github.com/Pylons/pyramid/pull/1876 |
| | | |
| | | - Add a new "view deriver" concept to Pyramid to allow framework authors to |
| | | inject elements into the standard Pyramid view pipeline and affect all |
| | | views in an application. This is similar to a decorator except that it |
| | | has access to options passed to ``config.add_view`` and can affect other |
| | | stages of the pipeline such as the raw response from a view or prior to |
| | | security checks. See https://github.com/Pylons/pyramid/pull/2021 |
| | | |
| | | - Allow a leading ``=`` on the key of the request param predicate. |
| | | For example, '=abc=1' is equivalent down to |
| | | ``request.params['=abc'] == '1'``. |
| | | See https://github.com/Pylons/pyramid/pull/1370 |
| | | |
| | | - A new ``request.invoke_exception_view(...)`` method which can be used to |
| | | invoke an exception view and get back a response. This is useful for |
| | | rendering an exception view outside of the context of the excview tween |
| | | where you may need more control over the request. |
| | | See https://github.com/Pylons/pyramid/pull/2393 |
| | | |
| | | - Allow using variable substitutions like ``%(LOGGING_LOGGER_ROOT_LEVEL)s`` |
| | | for logging sections of the .ini file and populate these variables from |
| | | the ``pserve`` command line -- e.g.: |
| | | ``pserve development.ini LOGGING_LOGGER_ROOT_LEVEL=DEBUG`` |
| | | See https://github.com/Pylons/pyramid/pull/2399 |
| | | |
| | | Documentation Changes |
| | | --------------------- |
| | | |
| | | - A complete overhaul of the docs: |
| | | |
| | | - Use pip instead of easy_install. |
| | | - Become opinionated by preferring Python 3.4 or greater to simplify |
| | | installation of Python and its required packaging tools. |
| | | - Use venv for the tool, and virtual environment for the thing created, |
| | | instead of virtualenv. |
| | | - Use py.test and pytest-cov instead of nose and coverage. |
| | | - Further updates to the scaffolds as well as tutorials and their src files. |
| | | |
| | | See https://github.com/Pylons/pyramid/pull/2468 |
| | | |
| | | - A complete overhaul of the ``alchemy`` scaffold as well as the |
| | | Wiki2 SQLAlchemy + URLDispatch tutorial to introduce more modern features |
| | | into the usage of SQLAlchemy with Pyramid and provide a better starting |
| | | point for new projects. |
| | | See https://github.com/Pylons/pyramid/pull/2024 |
| | | |
| | | Bug Fixes |
| | | --------- |
| | | |
| | | - Fix ``pserve --browser`` to use the ``--server-name`` instead of the |
| | | app name when selecting a section to use. This was only working for people |
| | | who had server and app sections with the same name, for example |
| | | ``[app:main]`` and ``[server:main]``. |
| | | See https://github.com/Pylons/pyramid/pull/2292 |
| | | |
| | | Deprecations |
| | | ------------ |
| | | |
| | | - The ``check_csrf`` view predicate has been deprecated. Use the |
| | | new ``require_csrf`` option or the ``pyramid.require_default_csrf`` setting |
| | | to ensure that the ``BadCSRFToken`` exception is raised. |
| | | See https://github.com/Pylons/pyramid/pull/2413 |
| | | |
| | | - Support for Python 3.3 will be removed in Pyramid 1.8. |
| | | https://github.com/Pylons/pyramid/issues/2477 |
| | | |
| | | - Python 2.6 is no longer supported by Pyramid. See |
| | | https://github.com/Pylons/pyramid/issues/2368 |
| | | |
| | | - Dropped Python 3.2 support. |
| | | See https://github.com/Pylons/pyramid/pull/2256 |
| | | Documentation Changes |
| | | --------------------- |
| | |
| | | - Marcin Raczyński, 2016/01/26 |
| | | |
| | | - Arian Maykon de A. Diógenes, 2016/04/13 |
| | | |
| | | - Vincent Férotin, 2016/05/08 |
| | | |
| | | - Berker Peksag, 2016/05/16 |
| | |
| | | 1.7 (2016-05-19) |
| | | ================ |
| | | |
| | | - Fix a bug in the wiki2 tutorial where bcrypt is always expecting byte |
| | | strings. See https://github.com/Pylons/pyramid/pull/2576 |
| | | |
| | | - Simplify windows detection code and remove some duplicated data. |
| | | See https://github.com/Pylons/pyramid/pull/2585 and |
| | | https://github.com/Pylons/pyramid/pull/2586 |
| | | |
| | | 1.7b4 (2016-05-12) |
| | | ================== |
| | | |
| | | - Fixed the exception view tween to re-raise the original exception if |
| | | no exception view could be found to handle the exception. This better |
| | | allows tweens further up the chain to handle exceptions that were |
| | | left unhandled. Previously they would be converted into a |
| | | ``PredicateMismatch`` exception if predicates failed to allow the view to |
| | | handle the exception. |
| | | See https://github.com/Pylons/pyramid/pull/2567 |
| | | |
| | | - Exposed the ``pyramid.interfaces.IRequestFactory`` interface to mirror |
| | | the public ``pyramid.interfaces.IResponseFactory`` interface. |
| | | |
| | | 1.7b3 (2016-05-10) |
| | | ================== |
| | | |
| | | - Fix ``request.invoke_exception_view`` to raise an ``HTTPNotFound`` |
| | | exception if no view is matched. Previously ``None`` would be returned |
| | | if no views were matched and a ``PredicateMismatch`` would be raised if |
| | | a view "almost" matched (a view was found matching the context). |
| | | See https://github.com/Pylons/pyramid/pull/2564 |
| | | |
| | | - Add defaults for py.test configuration and coverage to all three scaffolds, |
| | | and update documentation accordingly. |
| | | See https://github.com/Pylons/pyramid/pull/2550 |
| | | |
| | | - Add ``linkcheck`` to ``Makefile`` for Sphinx. To check the documentation for |
| | | broken links, use the command ``make linkcheck |
| | | SPHINXBUILD=$VENV/bin/sphinx-build``. Also removed and fixed dozens of broken |
| | | external links. |
| | | |
| | | - Fix the internal runner for scaffold tests to ensure they work with pip |
| | | and py.test. |
| | | See https://github.com/Pylons/pyramid/pull/2565 |
| | | |
| | | 1.7b2 (2016-05-01) |
| | | ================== |
| | | |
| | | - Removed inclusion of pyramid_tm in development.ini for alchemy scaffold |
| | | See https://github.com/Pylons/pyramid/issues/2538 |
| | | |
| | | - A default permission set via ``config.set_default_permission`` will no |
| | | longer be enforced on an exception view. This has been the case for a while |
| | | with the default exception views (``config.add_notfound_view`` and |
| | | ``config.add_forbidden_view``), however for any other exception view a |
| | | developer had to remember to set ``permission=NO_PERMISSION_REQUIRED`` or |
| | | be surprised when things didn't work. It is still possible to force a |
| | | permission check on an exception view by setting the ``permission`` argument |
| | | manually to ``config.add_view``. This behavior is consistent with the new |
| | | CSRF features added in the 1.7 series. |
| | | See https://github.com/Pylons/pyramid/pull/2534 |
| | | |
| | | 1.7b1 (2016-04-25) |
| | | ================== |
| | | |
| | | - This release announces the beta period for 1.7. |
| | | |
| | | - Fix an issue where some files were being included in the alchemy scafffold |
| | | which had been removed from the 1.7 series. |
| | | See https://github.com/Pylons/pyramid/issues/2525 |
| | | |
| | | 1.7a2 (2016-04-19) |
| | | ================== |
| | | |
| | | Features |
| | | -------- |
| | | |
| | | - Automatic CSRF checks are now disabled by default on exception views. They |
| | | can be turned back on by setting the appropriate `require_csrf` option on |
| | | the view. |
| | | See https://github.com/Pylons/pyramid/pull/2517 |
| | | |
| | | - The automatic CSRF API was reworked to use a config directive for |
| | | setting the options. The ``pyramid.require_default_csrf`` setting is |
| | | no longer supported. Instead, a new ``config.set_default_csrf_options`` |
| | | directive has been introduced that allows the developer to specify |
| | | the default value for ``require_csrf`` as well as change the CSRF token, |
| | | header and safe request methods. The ``pyramid.csrf_trusted_origins`` |
| | | setting is still supported. |
| | | See https://github.com/Pylons/pyramid/pull/2518 |
| | | |
| | | Bug fixes |
| | | --------- |
| | | |
| | | - CSRF origin checks had a bug causing the checks to always fail. |
| | | See https://github.com/Pylons/pyramid/pull/2512 |
| | | |
| | | - Fix the test suite to pass on windows. |
| | | See https://github.com/Pylons/pyramid/pull/2520 |
| | | |
| | | 1.7a1 (2016-04-16) |
| | | ================== |
| | | |
| | | Backward Incompatibilities |
| | | -------------------------- |
| | | |
| | | - Following the Pyramid deprecation period (1.4 -> 1.6), |
| | | AuthTktAuthenticationPolicy's default hashing algorithm is changing from md5 |
| | | to sha512. If you are using the authentication policy and need to continue |
| | | using md5, please explicitly set hashalg to 'md5'. |
| | | |
| | | This change does mean that any existing auth tickets (and associated cookies) |
| | | will no longer be valid, and users will no longer be logged in, and have to |
| | | login to their accounts again. |
| | | |
| | | See https://github.com/Pylons/pyramid/pull/2496 |
| | | |
| | | - The ``check_csrf_token`` function no longer validates a csrf token in the |
| | | query string of a request. Only headers and request bodies are supported. |
| | | See https://github.com/Pylons/pyramid/pull/2500 |
| | | |
| | | Features |
| | | -------- |
| | | |
| | | - Added a new setting, ``pyramid.require_default_csrf`` which may be used |
| | | to turn on CSRF checks globally for every POST request in the application. |
| | | This should be considered a good default for websites built on Pyramid. |
| | | It is possible to opt-out of CSRF checks on a per-view basis by setting |
| | | ``require_csrf=False`` on those views. |
| | | See https://github.com/Pylons/pyramid/pull/2413 |
| | | |
| | | - Added a ``require_csrf`` view option which will enforce CSRF checks on any |
| | | request with an unsafe method as defined by RFC2616. If the CSRF check fails |
| | | a ``BadCSRFToken`` exception will be raised and may be caught by exception |
| | | views (the default response is a ``400 Bad Request``). This option should be |
| | | used in place of the deprecated ``check_csrf`` view predicate which would |
| | | normally result in unexpected ``404 Not Found`` response to the client |
| | | instead of a catchable exception. See |
| | | https://github.com/Pylons/pyramid/pull/2413 and |
| | | https://github.com/Pylons/pyramid/pull/2500 |
| | | |
| | | - Added an additional CSRF validation that checks the origin/referrer of a |
| | | request and makes sure it matches the current ``request.domain``. This |
| | | particular check is only active when accessing a site over HTTPS as otherwise |
| | | browsers don't always send the required information. If this additional CSRF |
| | | validation fails a ``BadCSRFOrigin`` exception will be raised and may be |
| | | caught by exception views (the default response is ``400 Bad Request``). |
| | | Additional allowed origins may be configured by setting |
| | | ``pyramid.csrf_trusted_origins`` to a list of domain names (with ports if on |
| | | a non standard port) to allow. Subdomains are not allowed unless the domain |
| | | name has been prefixed with a ``.``. See |
| | | https://github.com/Pylons/pyramid/pull/2501 |
| | | |
| | | - Added a new ``pyramid.session.check_csrf_origin`` API for validating the |
| | | origin or referrer headers against the request's domain. |
| | | See https://github.com/Pylons/pyramid/pull/2501 |
| | | |
| | | - Pyramid HTTPExceptions will now take into account the best match for the |
| | | clients Accept header, and depending on what is requested will return |
| | | text/html, application/json or text/plain. The default for */* is still |
| | | text/html, but if application/json is explicitly mentioned it will now |
| | | receive a valid JSON response. See |
| | | https://github.com/Pylons/pyramid/pull/2489 |
| | | |
| | | - A new event and interface (BeforeTraversal) has been introduced that will |
| | | notify listeners before traversal starts in the router. See |
| | | https://github.com/Pylons/pyramid/pull/2469 and |
| | | https://github.com/Pylons/pyramid/pull/1876 |
| | | |
| | | - Add a new "view deriver" concept to Pyramid to allow framework authors to |
| | | inject elements into the standard Pyramid view pipeline and affect all |
| | | views in an application. This is similar to a decorator except that it |
| | | has access to options passed to ``config.add_view`` and can affect other |
| | | stages of the pipeline such as the raw response from a view or prior to |
| | | security checks. See https://github.com/Pylons/pyramid/pull/2021 |
| | | |
| | | - Allow a leading ``=`` on the key of the request param predicate. |
| | | For example, '=abc=1' is equivalent down to |
| | | ``request.params['=abc'] == '1'``. |
| | | See https://github.com/Pylons/pyramid/pull/1370 |
| | | |
| | | - A new ``request.invoke_exception_view(...)`` method which can be used to |
| | | invoke an exception view and get back a response. This is useful for |
| | | rendering an exception view outside of the context of the excview tween |
| | | where you may need more control over the request. |
| | | See https://github.com/Pylons/pyramid/pull/2393 |
| | | |
| | | - Allow using variable substitutions like ``%(LOGGING_LOGGER_ROOT_LEVEL)s`` |
| | | for logging sections of the .ini file and populate these variables from |
| | | the ``pserve`` command line -- e.g.: |
| | | ``pserve development.ini LOGGING_LOGGER_ROOT_LEVEL=DEBUG`` |
| | | See https://github.com/Pylons/pyramid/pull/2399 |
| | | |
| | | Documentation Changes |
| | | --------------------- |
| | | |
| | | - A complete overhaul of the docs: |
| | | |
| | | - Use pip instead of easy_install. |
| | | - Become opinionated by preferring Python 3.4 or greater to simplify |
| | | installation of Python and its required packaging tools. |
| | | - Use venv for the tool, and virtual environment for the thing created, |
| | | instead of virtualenv. |
| | | - Use py.test and pytest-cov instead of nose and coverage. |
| | | - Further updates to the scaffolds as well as tutorials and their src files. |
| | | |
| | | See https://github.com/Pylons/pyramid/pull/2468 |
| | | |
| | | - A complete overhaul of the ``alchemy`` scaffold as well as the |
| | | Wiki2 SQLAlchemy + URLDispatch tutorial to introduce more modern features |
| | | into the usage of SQLAlchemy with Pyramid and provide a better starting |
| | | point for new projects. |
| | | See https://github.com/Pylons/pyramid/pull/2024 |
| | | |
| | | Bug Fixes |
| | | --------- |
| | | |
| | | - Fix ``pserve --browser`` to use the ``--server-name`` instead of the |
| | | app name when selecting a section to use. This was only working for people |
| | | who had server and app sections with the same name, for example |
| | | ``[app:main]`` and ``[server:main]``. |
| | | See https://github.com/Pylons/pyramid/pull/2292 |
| | | |
| | | Deprecations |
| | | ------------ |
| | | |
| | | - The ``check_csrf`` view predicate has been deprecated. Use the |
| | | new ``require_csrf`` option or the ``pyramid.require_default_csrf`` setting |
| | | to ensure that the ``BadCSRFToken`` exception is raised. |
| | | See https://github.com/Pylons/pyramid/pull/2413 |
| | | |
| | | - Support for Python 3.3 will be removed in Pyramid 1.8. |
| | | https://github.com/Pylons/pyramid/issues/2477 |
| | | |
| | | - Python 2.6 is no longer supported by Pyramid. See |
| | | https://github.com/Pylons/pyramid/issues/2368 |
| | | |
| | | - Dropped Python 3.2 support. |
| | | See https://github.com/Pylons/pyramid/pull/2256 |
| | | |
| | | 1.6 (2016-01-03) |
| | | ================ |
| | | |
| | |
| | | |
| | | .. image:: https://travis-ci.org/Pylons/pyramid.png?branch=master |
| | | :target: https://travis-ci.org/Pylons/pyramid |
| | | :alt: Master Travis CI Status |
| | | |
| | | .. image:: https://readthedocs.org/projects/pyramid/badge/?version=master |
| | | :target: http://docs.pylonsproject.org/projects/pyramid/en/master/ |
| | |
| | | Support and Documentation |
| | | ------------------------- |
| | | |
| | | See the `Pylons Project website <http://pylonsproject.org/>`_ to view |
| | | documentation, report bugs, and obtain support. |
| | | See `Pyramid Support and Development |
| | | <http://docs.pylonsproject.org/projects/pyramid/en/latest/#support-and-development>`_ |
| | | for documentation, reporting bugs, and getting support. |
| | | |
| | | Developing and Contributing |
| | | --------------------------- |
| | |
| | | - Alpha, beta, dev and similar statuses do not qualify whether a release is |
| | | major or minor. The term "pre-release" means alpha, beta, or dev. |
| | | |
| | | - A release is final when it is no longer pre-release. |
| | | |
| | | - A *major* release is where the first number either before or after the |
| | | first dot increases. Examples: 1.6 to 1.7a1, or 1.8 to 2.0. |
| | | |
| | | - A *minor* or *bug fix* release is where the number after the second dot |
| | | increases. Example: 1.6 to 1.6.1. |
| | | |
| | | Prepare new release branch |
| | | -------------------------- |
| | | |
| | | - Create a new release branch, incrementing the version number. |
| | | |
| | | - Do any necessary branch merges (e.g., master to branch, branch to master). |
| | | |
| | |
| | | |
| | | - Run tests on Windows if feasible. |
| | | |
| | | - Make sure all scaffold tests pass (Py 2.7, 3.3, 3.4, 3.5, pypy, and pypy3 on |
| | | UNIX; this doesn't work on Windows): |
| | | - Make sure all scaffold tests pass (Py 2.7, 3.3, 3.4, 3.5, and pypy on UNIX; |
| | | this doesn't work on Windows): |
| | | |
| | | $ ./scaffoldtests.sh |
| | | |
| | | - Ensure all features of the release are documented (audit CHANGES.txt or |
| | | communicate with contributors). |
| | | |
| | | - Change CHANGES.txt heading to reflect the new version number. |
| | | |
| | | - Copy relevant changes (delta bug fixes) from CHANGES.txt to |
| | | docs/whatsnew-X.X (if it's a major release). Minor releases should |
| | |
| | | branch, and previously released branch. Also in the previously released |
| | | branch only, uncomment the sections to enable pylons_sphinx_latesturl. |
| | | |
| | | - Change setup.py version to the new version number on both master and the new |
| | | branch. |
| | | |
| | | - Change CHANGES.txt heading to reflect the new version number. |
| | | - Change setup.py version to the release version number. |
| | | |
| | | - Make sure PyPI long description renders (requires ``collective.dist`` |
| | | installed into your Python):: |
| | |
| | | $ python setup.py sdist bdist_wheel |
| | | $ twine upload dist/pyramid-X.X-* |
| | | |
| | | - Configure RTD to publish the new release version of the docs. |
| | | |
| | | Prepare master for further development (major releases only) |
| | | ------------------------------------------------------------ |
| | | |
| | | - Checkout master. |
| | | |
| | | - In CHANGES.txt, preserve headings but clear out content. Add heading |
| | | "unreleased" for the version number. |
| | | |
| | | - From the release branch, forward port the changes in CHANGES.txt to |
| | | HISTORY.txt. |
| | | |
| | | - In contributing.md, forward port branch descriptions from release branch. |
| | | |
| | | - In docs/conf.py, add a commented line under |
| | | pylons_sphinx_latesturl_pagename_overrides for the release. |
| | | |
| | | - Change setup.py version to the next version number. |
| | | |
| | | Update previous version (final releases only) |
| | | --------------------------------------------- |
| | | |
| | | - In docs/conf.py, update values under html_theme_options for in_progress and |
| | | outdated. Uncomment the sections to enable pylons_sphinx_latesturl. |
| | | |
| | | - Configure RTD to point the "latest" alias to the new release version of the |
| | | docs. |
| | | |
| | | Marketing and communications |
| | | ---------------------------- |
| | | |
| | | - Edit Pylons/pylonshq/templates/home/home.mako. |
| | | |
| | | - Edit Pylons/pylonshq/templates/home/inside.rst for major releases only. |
| | |
| | | - Edit `http://wiki.python.org/moin/WebFrameworks |
| | | <http://wiki.python.org/moin/WebFrameworks>`_. |
| | | |
| | | - Publish new version of docs. |
| | | - Announce to Twitter. |
| | | |
| | | ``` |
| | | Pyramid 1.x released. |
| | | |
| | | PyPI |
| | | https://pypi.python.org/pypi/pyramid/1.x |
| | | |
| | | === One time only for new version, first pre-release === |
| | | What's New |
| | | http://docs.pylonsproject.org/projects/pyramid/en/1.X-branch/whatsnew-1.X.html |
| | | === For all subsequent pre-releases === |
| | | Changes |
| | | http://docs.pylonsproject.org/projects/pyramid/en/1.X-branch/changes.html#version-yyyy-mm-dd |
| | | |
| | | Issues |
| | | https://github.com/Pylons/pyramid/issues |
| | | ``` |
| | | |
| | | - Announce to maillist. |
| | | |
| | | - Announce to Twitter. |
| | | |
| | | Announcement template |
| | | ---------------------- |
| | | |
| | | ``` |
| | | Pyramid 1.X.X has been released. |
| | | |
| | | Here are the changes: |
| | |
| | | <<changes>> |
| | | |
| | | A "What's New In Pyramid 1.X" document exists at |
| | | http://docs.pylonsproject.org/projects/pyramid/1.X/whatsnew-1.X.html . |
| | | http://docs.pylonsproject.org/projects/pyramid/1.X-branch/whatsnew-1.X.html . |
| | | |
| | | You will be able to see the 1.X release documentation (across all |
| | | alphas and betas, as well as when it eventually gets to final release) |
| | | at http://docs.pylonsproject.org/projects/pyramid/1.X/ . |
| | | at http://docs.pylonsproject.org/projects/pyramid/1.X-branch/ . |
| | | |
| | | You can install it via PyPI: |
| | | |
| | | easy_install Pyramid==1.X |
| | | pip install Pyramid==1.X |
| | | |
| | | Enjoy, and please report any issues you find to the issue tracker at |
| | | https://github.com/Pylons/pyramid/issues |
| | | |
| | | Thanks! |
| | | |
| | | - C |
| | | - Pyramid core developers |
| | | ``` |
New file |
| | |
| | | environment: |
| | | matrix: |
| | | - PYTHON: "C:\\Python35" |
| | | TOXENV: "py35" |
| | | |
| | | install: |
| | | - "%PYTHON%\\python.exe -m pip install tox" |
| | | |
| | | build: off |
| | | |
| | | test_script: |
| | | - "%PYTHON%\\Scripts\\tox.exe" |
| | |
| | | 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 |
| | | classified as "stable" or "latest". Actively maintained. |
| | | * [1.5-branch](https://github.com/Pylons/pyramid/tree/1.5-branch) - The oldest |
| | | actively maintained and stable branch. |
| | | development takes place. The default branch on GitHub. |
| | | * [1.7-branch](https://github.com/Pylons/pyramid/tree/1.7-branch) - The branch |
| | | classified as "stable" or "latest". |
| | | * [1.6-branch](https://github.com/Pylons/pyramid/tree/1.6-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. |
| | |
| | | PAPEROPT_letter = -D latex_paper_size=letter |
| | | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . |
| | | |
| | | .PHONY: help clean html web pickle htmlhelp latex changes linkcheck |
| | | .PHONY: help clean html text web pickle htmlhelp latex latexpdf changes linkcheck epub doctest |
| | | |
| | | help: |
| | | @echo "Please use \`make <target>' where <target> is one of" |
| | | @echo " html to make standalone HTML files" |
| | | @echo " pickle to make pickle files (usable by e.g. sphinx-web)" |
| | | @echo " htmlhelp to make HTML files and a HTML help project" |
| | | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" |
| | | @echo " changes to make an overview over all changed/added/deprecated items" |
| | | @echo " linkcheck to check all external links for integrity" |
| | | @echo " html to make standalone HTML files" |
| | | @echo " text to make text files" |
| | | @echo " pickle to make pickle files (usable by e.g. sphinx-web)" |
| | | @echo " htmlhelp to make HTML files and a HTML help project" |
| | | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" |
| | | @echo " latexpdf to make LaTeX files and run them through pdflatex" |
| | | @echo " changes to make an overview over all changed/added/deprecated items" |
| | | @echo " linkcheck to check all external links for integrity" |
| | | @echo " epub to make an epub" |
| | | @echo " doctest to run all doctests embedded in the documentation (if enabled)" |
| | | |
| | | clean: |
| | | -rm -rf $(BUILDDIR)/* |
| | |
| | | @echo |
| | | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." |
| | | |
| | | doctest: |
| | | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest |
| | | @echo "Testing of doctests in the sources finished, look at the " \ |
| | | "results in $(BUILDDIR)/doctest/output.txt." |
| | |
| | | |
| | | .. automethod:: set_authentication_policy |
| | | .. automethod:: set_authorization_policy |
| | | .. automethod:: set_default_csrf_options |
| | | .. automethod:: set_default_permission |
| | | .. automethod:: add_permission |
| | | |
| | |
| | | .. automethod:: add_traverser |
| | | .. automethod:: add_tween |
| | | .. automethod:: add_route_predicate |
| | | .. automethod:: add_subscriber_predicate |
| | | .. automethod:: add_view_predicate |
| | | .. automethod:: add_view_deriver |
| | | .. automethod:: set_request_factory |
| | |
| | | .. _html_api_documentation: |
| | | .. _api_documentation: |
| | | |
| | | API Documentation |
| | | ================= |
| | |
| | | .. autointerface:: IRenderer |
| | | :members: |
| | | |
| | | .. autointerface:: IRequestFactory |
| | | :members: |
| | | |
| | | .. autointerface:: IResponseFactory |
| | | :members: |
| | | |
| | |
| | | Book Content |
| | | ============ |
| | | |
| | | This book is divided into three major parts: |
| | | This book is divided into four major parts: |
| | | |
| | | :ref:`tutorials` |
| | | |
| | | Each tutorial builds a sample application or implements a set of |
| | | concepts with a sample; it then describes the application or |
| | | concepts in terms of the sample. You should read the tutorials if |
| | | you want a guided tour of :app:`Pyramid`. |
| | | |
| | | :ref:`narrative_documentation` |
| | | |
| | |
| | | out-of-order, or when you need only a reminder about a particular |
| | | topic while you're developing an application. |
| | | |
| | | :ref:`tutorials` |
| | | |
| | | Each tutorial builds a sample application or implements a set of |
| | | concepts with a sample; it then describes the application or |
| | | concepts in terms of the sample. You should read the tutorials if |
| | | you want a guided tour of :app:`Pyramid`. |
| | | |
| | | :ref:`api_documentation` |
| | | |
| | | Comprehensive reference material for every public API exposed by |
| | | :app:`Pyramid`. The API documentation is organized |
| | | alphabetically by module name. |
| | | |
| | | :ref:`pscripts_documentation` |
| | | |
| | | ``p*`` scripts included with :app:`Pyramid`. |
| | | |
| | | .. index:: |
| | | single: repoze.zope2 |
| | | single: Zope 3 |
| | |
| | | # 'whatsnew-1.4': 'index', |
| | | # 'whatsnew-1.5': 'index', |
| | | # 'whatsnew-1.6': 'index', |
| | | # 'whatsnew-1.7': 'index', |
| | | # 'tutorials/gae/index': 'index', |
| | | # 'api/chameleon_text': 'api', |
| | | # 'api/chameleon_zpt': 'api', |
| | |
| | | |
| | | URLs are presented using the following style: |
| | | |
| | | `Pylons <http://pylonsproject.org>`_ |
| | | `Pylons <http://www.pylonsproject.org>`_ |
| | | |
| | | References to sections and chapters are presented using the following |
| | | style: |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | $ $VENV/bin/py.test -q |
| | | |
| | | (See :term:`venv` for the meaning of ``$VENV``) |
| | | |
| | |
| | | |
| | | .. code-block:: doscon |
| | | |
| | | c:\examples> %VENV%\Scripts\py.test tutorial\tests.py -q |
| | | c:\examples> %VENV%\Scripts\py.test -q |
| | | |
| | | (See :term:`venv` for the meaning of ``%VENV%``) |
| | | |
| | |
| | | |
| | | .. code-block:: doscon |
| | | |
| | | > %VENV%\Scripts\py.test tutorial\tests.py -q |
| | | > %VENV%\Scripts\py.test -q |
| | | |
| | | When a command that should be typed on one line is too long to fit on a page, |
| | | the backslash ``\`` is used to indicate that the following printed line should |
| | |
| | | Copyright, Trademarks, and Attributions |
| | | ======================================= |
| | | |
| | | *The Pyramid Web Framework, Version 1.1* |
| | | "The Pyramid Web Framework, Version |version|" |
| | | |
| | | by Chris McDonough |
| | | |
| | |
| | | GitHub. |
| | | |
| | | Cover Designer: |
| | | Hugues Laflamme of `Kemeneur <http://www.kemeneur.com/>`_. |
| | | Hugues Laflamme of Kemeneur. |
| | | |
| | | Used with permission: |
| | | |
| | |
| | | ---------------- |
| | | |
| | | The print version of this book was produced using the `Sphinx |
| | | <http://sphinx.pocoo.org/>`_ documentation generation system and the |
| | | `LaTeX <http://www.latex-project.org/>`_ typesetting system. |
| | | <http://www.sphinx-doc.org/en/stable/>`_ documentation generation system and |
| | | the `LaTeX <http://www.latex-project.org/>`_ typesetting system. |
| | | |
| | | Contacting The Publisher |
| | | ------------------------ |
| | |
| | | and other business communications to `Agendaless Consulting |
| | | <mailto:webmaster@agendaless.com>`_. Please send software and other |
| | | technical queries to the `Pylons-devel mailing list |
| | | <http://groups.google.com/group/pylons-devel>`_. |
| | | <https://groups.google.com/forum/#!forum/pylons-devel>`_. |
| | | |
| | | HTML Version and Source Code |
| | | ---------------------------- |
| | |
| | | The source code for the examples used in this book are available |
| | | within the :app:`Pyramid` software distribution, always available |
| | | via https://github.com/Pylons/pyramid |
| | | |
| | |
| | | First, what's a "utility"? Well, for the purposes of this discussion, and |
| | | for the purpose of the code above, it's just not very important. If you |
| | | really want to know, you can read `this |
| | | <http://www.muthukadan.net/docs/zca.html#utility>`_. However, still, readers |
| | | <http://muthukadan.net/docs/zca.html#utility>`_. However, still, readers |
| | | of such code need to understand the concept in order to parse it. This is |
| | | problem number one. |
| | | |
| | |
| | | |
| | | 133KB |
| | | |
| | | pyramid/ (except for ``pyramd/tests`` and ``pyramid/scaffolds``) |
| | | pyramid/ (except for ``pyramid/tests`` and ``pyramid/scaffolds``) |
| | | |
| | | 812KB |
| | | |
| | |
| | | of reference for how various components in the common web framework might |
| | | hang together. But in the opinion of the author, "MVC" doesn't match the web |
| | | very well in general. Quoting from the `Model-View-Controller Wikipedia entry |
| | | <http://en.wikipedia.org/wiki/Model–view–controller>`_: |
| | | <https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller>`_: |
| | | |
| | | Though MVC comes in different flavors, control flow is generally as |
| | | follows: |
| | |
| | | +++++++++ |
| | | |
| | | :app:`Pyramid` performs automatic authorization checks only at :term:`view` |
| | | execution time. Zope 3 wraps context objects with a `security proxy |
| | | <http://wiki.zope.org/zope3/WhatAreSecurityProxies>`_, which causes Zope 3 also |
| | | to do security checks during attribute access. I like this, because it means: |
| | | execution time. Zope 3 wraps context objects with a security proxy, which |
| | | causes Zope 3 also to do security checks during attribute access. I like this, |
| | | because it means: |
| | | |
| | | #) When I use the security proxy machinery, I can have a view that |
| | | conditionally displays certain HTML elements (like form fields) or |
| | |
| | | Microframeworks have smaller Hello World programs |
| | | ------------------------------------------------- |
| | | |
| | | Self-described "microframeworks" exist. `Bottle <http://bottle.paws.de>`_ and |
| | | `Flask <http://flask.pocoo.org/>`_ are two that are becoming popular. `Bobo |
| | | <http://bobo.digicool.com/>`_ doesn't describe itself as a microframework, but |
| | | its intended user base is much the same. Many others exist. We've even (only as |
| | | a teaching tool, not as any sort of official project) `created one using |
| | | Pyramid <http://static.repoze.org/casts/videotags.html>`_. The videos use BFG, |
| | | a precursor to Pyramid, but the resulting code is `available for Pyramid too |
| | | <https://github.com/Pylons/groundhog>`_). Microframeworks are small frameworks |
| | | with one common feature: each allows its users to create a fully functional |
| | | application that lives in a single Python file. |
| | | Self-described "microframeworks" exist. `Bottle |
| | | <http://bottlepy.org/docs/dev/index.html>`_ and `Flask |
| | | <http://flask.pocoo.org/>`_ are two that are becoming popular. `Bobo |
| | | <http://bobo.digicool.com/en/latest/>`_ doesn't describe itself as a |
| | | microframework, but its intended user base is much the same. Many others exist. |
| | | We've even (only as a teaching tool, not as any sort of official project) |
| | | `created one using Pyramid <http://static.repoze.org/casts/videotags.html>`_. |
| | | The videos use BFG, a precursor to Pyramid, but the resulting code is |
| | | `available for Pyramid too <https://github.com/Pylons/groundhog>`_). |
| | | Microframeworks are small frameworks with one common feature: each allows its |
| | | users to create a fully functional application that lives in a single Python |
| | | file. |
| | | |
| | | Some developers and microframework authors point out that Pyramid's "hello |
| | | world" single-file program is longer (by about five lines) than the equivalent |
| | |
| | | from groundhog import Groundhog |
| | | app = Groundhog('myapp', 'seekrit') |
| | | |
| | | app.route('/admin') |
| | | @app.route('/admin') |
| | | def admin(): |
| | | return '<html>admin page</html>' |
| | | |
| | | app.route('/:action') |
| | | def action(): |
| | | @app.route('/:action') |
| | | def do_action(action): |
| | | if action == 'add': |
| | | return '<html>add</html>' |
| | | if action == 'delete': |
| | |
| | | from groundhog import Groundhog |
| | | app = Groundhog('myapp', 'seekrit') |
| | | |
| | | app.route('/:action') |
| | | def action(): |
| | | @app.route('/:action') |
| | | def do_action(action): |
| | | if action == 'add': |
| | | return '<html>add</html>' |
| | | if action == 'delete': |
| | | return '<html>delete</html>' |
| | | return app.abort(404) |
| | | |
| | | app.route('/admin') |
| | | @app.route('/admin') |
| | | def admin(): |
| | | return '<html>admin page</html>' |
| | | |
| | |
| | | # this is executed if the request method was GET or the |
| | | # credentials were invalid |
| | | |
| | | The `Pylons 1.X <http://pylonsproject.org>`_ web framework uses a similar |
| | | strategy. It calls these things "Stacked Object Proxies", so, for purposes |
| | | of this discussion, I'll do so as well. |
| | | The `Pylons 1.X |
| | | <http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/>`_ |
| | | web framework uses a similar strategy. It calls these things "Stacked Object |
| | | Proxies", so, for purposes of this discussion, I'll do so as well. |
| | | |
| | | Import statements in Python (``import foo``, ``from bar import baz``) are |
| | | most frequently performed to obtain a reference to an object defined globally |
| | |
| | | server = make_server('0.0.0.0', 8080, app) |
| | | server.serve_forever() |
| | | |
| | | Pyramid has ~ 700 pages of documentation (printed), covering topics from the |
| | | very basic to the most advanced. *Nothing* is left undocumented, quite |
| | | Pyramid has over 1200 pages of documentation (printed), covering topics from |
| | | the very basic to the most advanced. *Nothing* is left undocumented, quite |
| | | literally. It also has an *awesome*, very helpful community. Visit the |
| | | #pyramid IRC channel on freenode.net (irc://freenode.net#pyramid) and see. |
| | | `#pyramid IRC channel on freenode.net |
| | | <https://webchat.freenode.net/?channels=pyramid>`_ and see. |
| | | |
| | | Hate Zope |
| | | +++++++++ |
| | |
| | | ---------------- |
| | | |
| | | Other challenges are encouraged to be sent to the `Pylons-devel |
| | | <http://groups.google.com/group/pylons-devel>`_ maillist. We'll try to address |
| | | them by considering a design change, or at very least via exposition here. |
| | | <https://groups.google.com/forum/#!forum/pylons-devel>`_ maillist. We'll try |
| | | to address them by considering a design change, or at very least via exposition |
| | | here. |
| | |
| | | |
| | | Repoze |
| | | "Repoze" is essentially a "brand" of software developed by `Agendaless |
| | | Consulting <http://agendaless.com>`_ and a set of contributors. The |
| | | Consulting <https://agendaless.com>`_ and a set of contributors. The |
| | | term has no special intrinsic meaning. The project's `website |
| | | <http://repoze.org>`_ has more information. The software developed |
| | | "under the brand" is available in a `Subversion repository |
| | |
| | | You can use :term:`distribute` under Python 3 instead. |
| | | |
| | | distribute |
| | | `Distribute <http://packages.python.org/distribute/>`_ is a fork of |
| | | `Distribute <https://pythonhosted.org/distribute/>`_ is a fork of |
| | | :term:`setuptools` which runs on both Python 2 and Python 3. |
| | | |
| | | pkg_resources |
| | |
| | | :term:`principal` (or principals) associated with a request. |
| | | |
| | | WSGI |
| | | `Web Server Gateway Interface <http://www.wsgi.org/>`_. This is a |
| | | Python standard for connecting web applications to web servers, |
| | | similar to the concept of Java Servlets. :app:`Pyramid` requires |
| | | that your application be served as a WSGI application. |
| | | `Web Server Gateway Interface <http://wsgi.readthedocs.org/en/latest/>`_. |
| | | This is a Python standard for connecting web applications to web servers, |
| | | similar to the concept of Java Servlets. :app:`Pyramid` requires that |
| | | your application be served as a WSGI application. |
| | | |
| | | middleware |
| | | *Middleware* is a :term:`WSGI` concept. It is a WSGI component |
| | | that acts both as a server and an application. Interesting uses |
| | | for middleware exist, such as caching, content-transport |
| | | encoding, and other functions. See `WSGI.org <http://www.wsgi.org>`_ |
| | | or `PyPI <http://python.org/pypi>`_ to find middleware for your |
| | | application. |
| | | encoding, and other functions. See `WSGI.org |
| | | <http://wsgi.readthedocs.org/en/latest/>`_ or `PyPI |
| | | <https://pypi.python.org/pypi>`_ to find middleware for your application. |
| | | |
| | | pipeline |
| | | The :term:`PasteDeploy` term for a single configuration of a WSGI |
| | |
| | | `A web framework based on Zope 3 <http://grok.zope.org>`_. |
| | | |
| | | Django |
| | | `A full-featured Python web framework <http://djangoproject.com>`_. |
| | | `A full-featured Python web framework <https://www.djangoproject.com/>`_. |
| | | |
| | | Pylons |
| | | `A lightweight Python web framework <http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/>`_ |
| | | and a predecessor of Pyramid. |
| | | |
| | | ZODB |
| | | `Zope Object Database <http://zodb.org>`_, a |
| | | persistent Python object store. |
| | | `Zope Object Database <http://www.zodb.org/en/latest/>`_, a persistent |
| | | Python object store. |
| | | |
| | | WebOb |
| | | `WebOb <http://webob.org>`_ is a WSGI request/response |
| | |
| | | the box in ZPT and text flavors. |
| | | |
| | | ZPT |
| | | The `Zope Page Template <http://wiki.zope.org/ZPT/FrontPage>`_ |
| | | The `Zope Page Template <http://docs.zope.org/zope2/zope2book/ZPT.html>`_ |
| | | templating language. |
| | | |
| | | METAL |
| | | `Macro Expansion for TAL <http://wiki.zope.org/ZPT/METAL>`_, a |
| | | part of :term:`ZPT` which makes it possible to share common look |
| | | and feel between templates. |
| | | `Macro Expansion for TAL |
| | | <http://docs.zope.org/zope2/zope2book/AppendixC.html#metal-overview>`_, a |
| | | part of :term:`ZPT` which makes it possible to share common look and feel |
| | | between templates. |
| | | |
| | | Genshi |
| | | An `XML templating language <http://pypi.python.org/pypi/Genshi/>`_ |
| | | An `XML templating language <https://pypi.python.org/pypi/Genshi/>`_ |
| | | by Christopher Lenz. |
| | | |
| | | Jinja2 |
| | | A `text templating language <http://jinja.pocoo.org/2/>`_ by Armin |
| | | Ronacher. |
| | | A `text templating language <http://jinja.pocoo.org/>`_ by Armin Ronacher. |
| | | |
| | | Routes |
| | | A `system by Ben Bangert <http://routes.groovie.org/>`_ which |
| | | parses URLs and compares them against a number of user defined |
| | | mappings. The URL pattern matching syntax in :app:`Pyramid` is |
| | | inspired by the Routes syntax (which was inspired by Ruby On |
| | | Rails pattern syntax). |
| | | A `system by Ben Bangert <http://routes.readthedocs.org/en/latest/>`_ |
| | | which parses URLs and compares them against a number of user defined |
| | | mappings. The URL pattern matching syntax in :app:`Pyramid` is inspired by |
| | | the Routes syntax (which was inspired by Ruby On Rails pattern syntax). |
| | | |
| | | route |
| | | A single pattern matched by the :term:`url dispatch` subsystem, |
| | |
| | | |
| | | Zope Component Architecture |
| | | The `Zope Component Architecture |
| | | <http://www.muthukadan.net/docs/zca.html>`_ (aka ZCA) is a system |
| | | <http://muthukadan.net/docs/zca.html>`_ (aka ZCA) is a system |
| | | which allows for application pluggability and complex dispatching |
| | | based on objects which implement an :term:`interface`. |
| | | :app:`Pyramid` uses the ZCA "under the hood" to perform view |
| | |
| | | subpath. See :ref:`star_subpath` for more information. |
| | | |
| | | interface |
| | | A `Zope interface <http://pypi.python.org/pypi/zope.interface>`_ |
| | | A `Zope interface <https://pypi.python.org/pypi/zope.interface>`_ |
| | | object. In :app:`Pyramid`, an interface may be attached to a |
| | | :term:`resource` object or a :term:`request` object in order to |
| | | identify that the object is "of a type". Interfaces are used |
| | |
| | | |
| | | repoze.catalog |
| | | An indexing and search facility (fielded and full-text) based on |
| | | `zope.index <http://pypi.python.org/pypi/zope.index>`_. See `the |
| | | `zope.index <https://pypi.python.org/pypi/zope.index>`_. See `the |
| | | documentation <http://docs.repoze.org/catalog>`_ for more |
| | | information. |
| | | |
| | | repoze.who |
| | | `Authentication middleware <http://docs.repoze.org/who>`_ for |
| | | :term:`WSGI` applications. It can be used by :app:`Pyramid` to |
| | | `Authentication middleware <http://repozewho.readthedocs.org/en/latest/>`_ |
| | | for :term:`WSGI` applications. It can be used by :app:`Pyramid` to |
| | | provide authentication information. |
| | | |
| | | repoze.workflow |
| | |
| | | serialization format. |
| | | |
| | | jQuery |
| | | A popular `Javascript library <http://jquery.org>`_. |
| | | A popular `Javascript library <https://jquery.org>`_. |
| | | |
| | | renderer |
| | | A serializer which converts non-:term:`Response` return values from a |
| | |
| | | :ref:`adding_and_overriding_renderers` for more information. |
| | | |
| | | mod_wsgi |
| | | `mod_wsgi <http://code.google.com/p/modwsgi/>`_ is an Apache |
| | | module developed by Graham Dumpleton. It allows :term:`WSGI` |
| | | applications (such as applications developed using |
| | | :app:`Pyramid`) to be served using the Apache web server. |
| | | `mod_wsgi <https://code.google.com/archive/p/modwsgi>`_ is an Apache |
| | | module developed by Graham Dumpleton. It allows :term:`WSGI` applications |
| | | (such as applications developed using :app:`Pyramid`) to be served using |
| | | the Apache web server. |
| | | |
| | | view predicate |
| | | An argument to a :term:`view configuration` which evaluates to |
| | |
| | | |
| | | .. seealso:: |
| | | |
| | | See also `PEP 318 <http://www.python.org/dev/peps/pep-0318/>`_. |
| | | See also `PEP 318 <https://www.python.org/dev/peps/pep-0318/>`_. |
| | | |
| | | configuration declaration |
| | | An individual method call made to a :term:`configuration directive`, |
| | |
| | | thread local |
| | | A thread-local variable is one which is essentially a global variable |
| | | in terms of how it is accessed and treated, however, each `thread |
| | | <http://en.wikipedia.org/wiki/Thread_(computer_science)>`_ used by the |
| | | <https://en.wikipedia.org/wiki/Thread_(computer_science)>`_ used by the |
| | | application may have a different value for this same "global" variable. |
| | | :app:`Pyramid` uses a small number of thread local variables, as |
| | | described in :ref:`threadlocals_chapter`. |
| | |
| | | :ref:`multidict_narr` and :class:`pyramid.interfaces.IMultiDict`. |
| | | |
| | | PyPI |
| | | `The Python Package Index <http://pypi.python.org/pypi>`_, a |
| | | collection of software available for Python. |
| | | `The Python Package Index <https://pypi.python.org/pypi>`_, a collection |
| | | of software available for Python. |
| | | |
| | | Agendaless Consulting |
| | | A consulting organization formed by Paul Everitt, Tres Seaver, |
| | |
| | | |
| | | .. seealso:: |
| | | |
| | | See also `Agendaless Consulting <http://agendaless.com>`_. |
| | | See also `Agendaless Consulting <https://agendaless.com>`_. |
| | | |
| | | Jython |
| | | A `Python implementation <http://www.jython.org/>`_ written for |
| | | the Java Virtual Machine. |
| | | |
| | | Python |
| | | The `programming language <http://python.org>`_ in which |
| | | The `programming language <https://www.python.org>`_ in which |
| | | :app:`Pyramid` is written. |
| | | |
| | | CPython |
| | |
| | | subsystems used by :app:`Pyramid`. |
| | | |
| | | Google App Engine |
| | | `Google App Engine <http://code.google.com/appengine/>`_ (aka |
| | | `Google App Engine <https://cloud.google.com/appengine/>`_ (aka |
| | | "GAE") is a Python application hosting service offered by Google. |
| | | :app:`Pyramid` runs on GAE. |
| | | |
| | |
| | | can be used as global application values. |
| | | |
| | | WebTest |
| | | `WebTest <http://pythonpaste.org/webtest/>`_ is a package which can help |
| | | `WebTest <http://webtest.pythonpaste.org/en/latest/>`_ is a package which can help |
| | | you write functional tests for your WSGI application. |
| | | |
| | | view mapper |
| | |
| | | |
| | | ZCML |
| | | `Zope Configuration Markup Language |
| | | <http://www.muthukadan.net/docs/zca.html#zcml>`_, an XML dialect |
| | | <http://muthukadan.net/docs/zca.html#zcml>`_, an XML dialect |
| | | used by Zope and :term:`pyramid_zcml` for configuration tasks. |
| | | |
| | | pyramid_handlers |
| | | An add-on package which allows :app:`Pyramid` users to create classes |
| | | that are analogues of Pylons 1 "controllers". See |
| | | http://docs.pylonsproject.org/projects/pyramid_handlers/dev/ . |
| | | http://docs.pylonsproject.org/projects/pyramid_handlers/en/latest/. |
| | | |
| | | pyramid_jinja2 |
| | | :term:`Jinja2` templating system bindings for Pyramid, documented at |
| | | http://docs.pylonsproject.org/projects/pyramid_jinja2/dev/ . This |
| | | package also includes a scaffold named |
| | | ``pyramid_jinja2_starter``, which creates an application package based |
| | | on the Jinja2 templating system. |
| | | http://docs.pylonsproject.org/projects/pyramid_jinja2/en/latest/. This |
| | | package also includes a scaffold named ``pyramid_jinja2_starter``, which |
| | | creates an application package based on the Jinja2 templating system. |
| | | |
| | | Akhet |
| | | `Akhet <http://docs.pylonsproject.org/projects/akhet/en/latest/>`_ is a |
| | |
| | | |
| | | distutils |
| | | The standard system for packaging and distributing Python packages. See |
| | | http://docs.python.org/distutils/index.html for more information. |
| | | https://docs.python.org/2/distutils/index.html for more information. |
| | | :term:`setuptools` is actually an *extension* of the Distutils. |
| | | |
| | | exception response |
| | |
| | | used in production applications, because the logger can be configured to |
| | | log to a file, to UNIX syslog, to the Windows Event Log, or even to |
| | | email. See its `documentation |
| | | <http://docs.pylonsproject.org/projects/pyramid_exclog/dev/>`_. |
| | | <http://docs.pylonsproject.org/projects/pyramid_exclog/en/latest/>`_. |
| | | |
| | | console script |
| | | A script written to the ``bin`` (on UNIX, or ``Scripts`` on Windows) |
| | |
| | | ========================= |
| | | |
| | | :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/>`_. |
| | | developed as part of the `Pylons Project <http://www.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: |
| | |
| | | this application works. |
| | | |
| | | |
| | | .. _html_getting_started: |
| | | .. _getting_started: |
| | | |
| | | Getting Started |
| | | =============== |
| | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | |
| | | tutorials/wiki2/index.rst |
| | | tutorials/wiki/index.rst |
| | | tutorials/modwsgi/index.rst |
| | | tutorials/wiki2/index |
| | | tutorials/wiki/index |
| | | tutorials/modwsgi/index |
| | | |
| | | |
| | | .. _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. |
| | | The `Pyramid website <https://trypyramid.com/resources.html>`_ is the main |
| | | entry point to :app:`Pyramid` web framework resources for 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>`_. |
| | | `Pylons-discuss maillist |
| | | <https://groups.google.com/forum/#!forum/pylons-discuss>`_ or join the |
| | | `#pyramid IRC channel |
| | | <https://webchat.freenode.net/?channels=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 |
| | |
| | | ``p*`` Scripts Documentation |
| | | ============================ |
| | | |
| | | ``p*`` scripts included with :app:`Pyramid`:. |
| | | ``p*`` scripts included with :app:`Pyramid`. |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | |
| | | copyright.rst |
| | | conventions.rst |
| | | authorintro.rst |
| | | copyright |
| | | conventions |
| | | authorintro |
| | | designdefense |
| | | |
| | | .. mainmatter:: |
| | | |
| | | .. _tutorials: |
| | | |
| | | Tutorials |
| | | @@@@@@@@@ |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | |
| | | quick_tour |
| | | quick_tutorial/index |
| | | tutorials/wiki2/index |
| | | tutorials/wiki/index |
| | | tutorials/modwsgi/index |
| | | |
| | | .. _narrative_documentation: |
| | | |
| | |
| | | narr/threadlocals |
| | | narr/zca |
| | | |
| | | .. _tutorials: |
| | | |
| | | Tutorials |
| | | @@@@@@@@@ |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | |
| | | tutorials/wiki2/index.rst |
| | | tutorials/wiki/index.rst |
| | | tutorials/modwsgi/index.rst |
| | | |
| | | .. _api_documentation: |
| | | |
| | | API Documentation |
| | | @@@@@@@@@@@@@@@@@ |
| | | |
| | |
| | | :maxdepth: 1 |
| | | :glob: |
| | | |
| | | api/index |
| | | api/* |
| | | |
| | | |
| | | ``p*`` Scripts Documentation |
| | | @@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | :glob: |
| | | |
| | | pscripts/index |
| | | pscripts/* |
| | | |
| | | |
| | | .. backmatter:: |
| | | |
| | | Change History |
| | | @@@@@@@@@@@@@@ |
| | | |
| | | .. toctree:: |
| | | :maxdepth: 1 |
| | | |
| | | whatsnew-1.7 |
| | | whatsnew-1.6 |
| | | whatsnew-1.5 |
| | | whatsnew-1.4 |
| | | whatsnew-1.3 |
| | | whatsnew-1.2 |
| | | whatsnew-1.1 |
| | | whatsnew-1.0 |
| | | changes |
| | | |
| | | |
| | | Glossary and Index |
| | | @@@@@@@@@@@@@@@@@@ |
| | | |
| | |
| | | - :meth:`pyramid.config.Configurator.add_view_predicate` |
| | | - :meth:`pyramid.config.Configurator.add_view_deriver` |
| | | - :meth:`pyramid.config.Configurator.set_authorization_policy` |
| | | - :meth:`pyramid.config.Configurator.set_default_csrf_options` |
| | | - :meth:`pyramid.config.Configurator.set_default_permission` |
| | | - :meth:`pyramid.config.Configurator.set_view_mapper` |
| | | |
| | |
| | | server to present an application to a requestor. :term:`WSGI` is a protocol |
| | | that allows servers to talk to Python applications. We don't discuss |
| | | :term:`WSGI` in any depth within this book, but you can learn more about it by |
| | | visiting `wsgi.org <http://wsgi.org>`_. |
| | | reading its `documentation <http://wsgi.readthedocs.org/en/latest/>`_. |
| | | |
| | | The :app:`Pyramid` application object, in particular, is an instance of a class |
| | | representing a :app:`Pyramid` :term:`router`. It has a reference to the |
| | |
| | | ``reify=True``. This way, we eliminate the overhead of running the function |
| | | multiple times. |
| | | |
| | | .. testsetup:: group1 |
| | | |
| | | from pyramid.config import Configurator |
| | | |
| | | |
| | | def total(request, *args): |
| | | return sum(args) |
| | | |
| | | |
| | | def prop(request): |
| | | print("getting the property") |
| | | return "the property" |
| | | |
| | | |
| | | |
| | | config = Configurator() |
| | | config.add_request_method(total) |
| | | config.add_request_method(prop, reify=True) |
| | | config.commit() |
| | | |
| | | from pyramid.scripting import prepare |
| | | request = prepare(registry=config.registry)["request"] |
| | | |
| | | .. doctest:: group1 |
| | | |
| | | >>> request.total(1, 2, 3) |
| | | 6 |
| | | >>> request.prop |
| | | getting the property |
| | | the property |
| | | 'the property' |
| | | >>> request.prop |
| | | the property |
| | | 'the property' |
| | | |
| | | To not cache the result of ``request.prop``, set ``property=True`` instead of |
| | | ``reify=True``. |
| | |
| | | |
| | | We attach and cache an object named ``extra`` to the ``request`` object. |
| | | |
| | | .. testsetup:: group2 |
| | | |
| | | from pyramid.config import Configurator |
| | | from pyramid.decorator import reify |
| | | |
| | | class ExtraStuff(object): |
| | | |
| | | def __init__(self, request): |
| | | self.request = request |
| | | |
| | | def total(self, *args): |
| | | return sum(args) |
| | | |
| | | # use @property if you don't want to cache the result |
| | | @reify |
| | | def prop(self): |
| | | print("getting the property") |
| | | return "the property" |
| | | |
| | | config = Configurator() |
| | | config.add_request_method(ExtraStuff, 'extra', reify=True) |
| | | config.commit() |
| | | |
| | | from pyramid.scripting import prepare |
| | | request = prepare(registry=config.registry)["request"] |
| | | |
| | | .. doctest:: group2 |
| | | |
| | | >>> request.extra.total(1, 2, 3) |
| | | 6 |
| | | >>> request.extra.prop |
| | | getting the property |
| | | the property |
| | | 'the property' |
| | | >>> request.extra.prop |
| | | the property |
| | | 'the property' |
| | | |
| | | |
| | | .. index:: |
| | | single: response factory |
| | |
| | | ``csrf_view`` |
| | | |
| | | Used to check the CSRF token provided in the request. This element is a |
| | | no-op if both the ``require_csrf`` view option and the |
| | | ``pyramid.require_default_csrf`` setting are disabled. |
| | | no-op if ``require_csrf`` view option is not ``True``. Note there will |
| | | always be a ``require_csrf`` option if a default value was assigned via |
| | | :meth:`pyramid.config.Configurator.set_default_csrf_options`. |
| | | |
| | | ``owrapped_view`` |
| | | |
| | |
| | | ``myapplication/locale/es/LC_MESSAGES/myapplication.po``. |
| | | |
| | | Once the file is there, it can be worked on by a human translator. One tool |
| | | which may help with this is `Poedit <http://www.poedit.net/>`_. |
| | | which may help with this is `Poedit <https://poedit.net/>`_. |
| | | |
| | | Note that :app:`Pyramid` itself ignores the existence of all ``.po`` files. |
| | | For a running application to have translations available, a ``.mo`` file must |
| | |
| | | |
| | | The features represented by attributes of the ``i18n`` namespace of Chameleon |
| | | will also consult the :app:`Pyramid` translations. See |
| | | http://chameleon.readthedocs.org/en/latest/reference.html#id50. |
| | | http://chameleon.readthedocs.org/en/latest/reference.html#translation-i18n. |
| | | |
| | | .. note:: |
| | | |
| | |
| | | |
| | | If your Windows system doesn't have a Python interpreter, you'll need to |
| | | install it by downloading a Python 3.x-series interpreter executable from |
| | | `python.org's download section <http://python.org/download/>`_ (the files |
| | | `python.org's download section <https://www.python.org/downloads/>`_ (the files |
| | | labeled "Windows Installer"). Once you've downloaded it, double click on the |
| | | executable and accept the defaults during the installation process. You may |
| | | also need to download and install the Python for Windows extensions. |
| | |
| | | Windows <python:using-on-windows>` for full details. |
| | | |
| | | .. seealso:: Download and install the `Python for Windows extensions |
| | | <http://sourceforge.net/projects/pywin32/files/pywin32/>`_. Carefully read |
| | | <https://sourceforge.net/projects/pywin32/files/pywin32/>`_. Carefully read |
| | | the README.txt file at the end of the list of builds, and follow its |
| | | directions. Make sure you get the proper 32- or 64-bit build and Python |
| | | version. |
| | |
| | | JSON-RPC, let you integrate with jQuery Mobile, etc. |
| | | |
| | | Examples: |
| | | http://docs.pylonsproject.org/en/latest/docs/pyramid.html#pyramid-add-on-documentation |
| | | https://trypyramid.com/resources-extending-pyramid.html |
| | | |
| | | Class-based and function-based views |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | |
| | | |
| | | Example: Visit irc\://freenode.net#pyramid (the ``#pyramid`` channel on |
| | | irc.freenode.net in an IRC client) or the pylons-discuss maillist at |
| | | http://groups.google.com/group/pylons-discuss/. |
| | | https://groups.google.com/forum/#!forum/pylons-discuss. |
| | | |
| | | Documentation |
| | | ~~~~~~~~~~~~~ |
| | |
| | | |
| | | :app:`Pyramid` is a member of the collection of software published under the |
| | | Pylons Project. Pylons software is written by a loose-knit community of |
| | | contributors. The `Pylons Project website <http://pylonsproject.org>`_ |
| | | contributors. The `Pylons Project website <http://www.pylonsproject.org>`_ |
| | | includes details about how :app:`Pyramid` relates to the Pylons Project. |
| | | |
| | | .. index:: |
| | |
| | | |
| | | Other Python web frameworks advertise themselves as members of a class of web |
| | | frameworks named `model-view-controller |
| | | <http://en.wikipedia.org/wiki/Model–view–controller>`_ frameworks. Insofar as |
| | | this term has been claimed to represent a class of web frameworks, |
| | | :app:`Pyramid` also generally fits into this class. |
| | | <https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller>`_ |
| | | frameworks. Insofar as this term has been claimed to represent a class of web |
| | | frameworks, :app:`Pyramid` also generally fits into this class. |
| | | |
| | | .. sidebar:: You Say :app:`Pyramid` is MVC, but Where's the Controller? |
| | | |
| | |
| | | |
| | | The permission name passed to ``set_default_permission``. |
| | | |
| | | ``default csrf options`` |
| | | |
| | | There will be one and only one introspectable in the ``default csrf options`` |
| | | category. It represents a call to the |
| | | :meth:`pyramid.config.Configurator.set_default_csrf_options` method. It |
| | | will have the following data. |
| | | |
| | | ``require_csrf`` |
| | | |
| | | The default value for ``require_csrf`` if left unspecified on calls to |
| | | :meth:`pyramid.config.Configurator.add_view`. |
| | | |
| | | ``token`` |
| | | |
| | | The name of the token searched in ``request.POST`` to find a valid CSRF |
| | | token. |
| | | |
| | | ``header`` |
| | | |
| | | The name of the request header searched to find a valid CSRF token. |
| | | |
| | | ``safe_methods`` |
| | | |
| | | The list of HTTP methods considered safe and exempt from CSRF checks. |
| | | |
| | | ``views`` |
| | | |
| | | Each introspectable in the ``views`` category represents a call to |
| | |
| | | |
| | | To log or email exceptions generated by your :app:`Pyramid` application, use |
| | | the :term:`pyramid_exclog` package. Details about its configuration are in its |
| | | `documentation <http://docs.pylonsproject.org/projects/pyramid_exclog/dev/>`_. |
| | | `documentation |
| | | <http://docs.pylonsproject.org/projects/pyramid_exclog/en/latest/>`_. |
| | | |
| | | .. index:: |
| | | single: TransLogger |
| | |
| | | |
| | | .. 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. |
| | | |
| | | Traversal is an alternative to :term:`URL dispatch` which allows :app:`Pyramid` |
| | | applications to map URLs to code. |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test myproject/tests.py -q |
| | | $ $VENV/bin/py.test -q |
| | | |
| | | On Windows: |
| | | |
| | | .. code-block:: doscon |
| | | |
| | | > %VENV%\Scripts\py.test myproject\tests.py -q |
| | | > %VENV%\Scripts\py.test -q |
| | | |
| | | Here's sample output from a test run on UNIX: |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test myproject/tests.py -q |
| | | $ $VENV/bin/py.test -q |
| | | .. |
| | | 2 passed in 0.47 seconds |
| | | |
| | |
| | | The ``-q`` option is passed to the ``py.test`` command to limit the output |
| | | to a stream of dots. If you don't pass ``-q``, you'll see verbose test |
| | | result output (which normally isn't very useful). |
| | | |
| | | Alternatively, if you'd like to see test coverage, pass the ``--cov`` option |
| | | to ``py.test``: |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test --cov -q |
| | | |
| | | Scaffolds include configuration defaults for ``py.test`` and test coverage. |
| | | These configuration files are ``pytest.ini`` and ``.coveragerc``, located at |
| | | the root of your package. Without these defaults, we would need to specify the |
| | | path to the module on which we want to run tests and coverage. |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test --cov=myproject myproject/tests.py -q |
| | | |
| | | .. seealso:: See py.test's documentation for :ref:`pytest:usage` or invoke |
| | | ``py.test -h`` to see its full set of options. |
| | | |
| | | |
| | | .. index:: |
| | | single: running an application |
| | |
| | | The sections after ``# logging configuration`` represent Python's standard |
| | | library :mod:`logging` module configuration for your application. These |
| | | sections are passed to the `logging module's config file configuration engine |
| | | <http://docs.python.org/howto/logging.html#configuring-logging>`_ when the |
| | | <https://docs.python.org/2/howto/logging.html#configuring-logging>`_ when the |
| | | ``pserve`` or ``pshell`` commands are executed. The default configuration |
| | | sends application logging output to the standard error output of your terminal. |
| | | For more information about logging configuration, see :ref:`logging_chapter`. |
| | |
| | | ``MANIFEST.in``, an sdist of your Pyramid project will include ``.txt`` files, |
| | | ``.ini`` files, ``.rst`` files, graphics files, and template files, as well as |
| | | ``.py`` files. See |
| | | http://docs.python.org/distutils/sourcedist.html#the-manifest-in-template for |
| | | more information about the syntax and usage of ``MANIFEST.in``. |
| | | https://docs.python.org/2/distutils/sourcedist.html#the-manifest-in-template |
| | | for more information about the syntax and usage of ``MANIFEST.in``. |
| | | |
| | | Without the presence of a ``MANIFEST.in`` file or without checking your source |
| | | code into a version control repository, ``setup.py sdist`` places only *Python |
| | |
| | | ``MANIFEST.in`` and you don't make use of a setuptools-compatible version |
| | | control system, you'll need to edit the ``MANIFEST.in`` file and include the |
| | | statements necessary to include your new files. See |
| | | http://docs.python.org/distutils/sourcedist.html#principle for more information |
| | | about how to do this. |
| | | https://docs.python.org/2/distutils/sourcedist.html#principle for more |
| | | information about how to do this. |
| | | |
| | | You can also delete ``MANIFEST.in`` from your project and rely on a setuptools |
| | | feature which simply causes all files checked into a version control system to |
| | |
| | | field. The version number is specified in the ``version`` value. A short |
| | | description is provided in the ``description`` field. The ``long_description`` |
| | | is conventionally the content of the ``README`` and ``CHANGES`` files appended |
| | | together. The ``classifiers`` field is a list of `Trove |
| | | <http://pypi.python.org/pypi?%3Aaction=list_classifiers>`_ classifiers |
| | | describing your application. ``author`` and ``author_email`` are text fields |
| | | which probably don't need any description. ``url`` is a field that should |
| | | point at your application project's URL (if any). ``packages=find_packages()`` |
| | | causes all packages within the project to be found when packaging the |
| | | application. ``include_package_data`` will include non-Python files when the |
| | | application is packaged if those files are checked into version control. |
| | | ``zip_safe=False`` indicates that this package is not safe to use as a zipped |
| | | egg; instead it will always unpack as a directory, which is more convenient. |
| | | ``install_requires`` indicate that this package depends on the ``pyramid`` |
| | | package. ``extras_require`` is a Python dictionary that defines what is |
| | | required to be installed for running tests. We examined ``entry_points`` in our |
| | | discussion of the ``development.ini`` file; this file defines the ``main`` |
| | | entry point that represents our project's application. |
| | | together. The ``classifiers`` field is a list of `Trove classifiers |
| | | <https://pypi.python.org/pypi?%3Aaction=list_classifiers>`_ describing your |
| | | application. ``author`` and ``author_email`` are text fields which probably |
| | | don't need any description. ``url`` is a field that should point at your |
| | | application project's URL (if any). ``packages=find_packages()`` causes all |
| | | packages within the project to be found when packaging the application. |
| | | ``include_package_data`` will include non-Python files when the application is |
| | | packaged if those files are checked into version control. ``zip_safe=False`` |
| | | indicates that this package is not safe to use as a zipped egg; instead it will |
| | | always unpack as a directory, which is more convenient. ``install_requires`` |
| | | indicates that this package depends on the ``pyramid`` package. |
| | | ``extras_require`` is a Python dictionary that defines what is required to be |
| | | installed for running tests. We examined ``entry_points`` in our discussion of |
| | | the ``development.ini`` file; this file defines the ``main`` entry point that |
| | | represents our project's application. |
| | | |
| | | Usually you only need to think about the contents of the ``setup.py`` file when |
| | | distributing your application to other people, when adding Python package |
| | |
| | | :linenos: |
| | | |
| | | This sample ``tests.py`` file has one unit test and one functional test defined |
| | | within it. These tests are executed when you run ``py.test myproject/tests.py |
| | | -q``. You may add more tests here as you build your application. You are not |
| | | required to write tests to use :app:`Pyramid`. This file is simply provided for |
| | | convenience and example. |
| | | within it. These tests are executed when you run ``py.test -q``. You may add |
| | | more tests here as you build your application. You are not required to write |
| | | tests to use :app:`Pyramid`. This file is simply provided for convenience and |
| | | example. |
| | | |
| | | See :ref:`testing_chapter` for more information about writing :app:`Pyramid` |
| | | unit tests. |
| | |
| | | .. versionadded:: 1.1 |
| | | |
| | | :class:`pyramid.renderers.JSONP` is a `JSONP |
| | | <http://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper which implements |
| | | <https://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper which implements |
| | | a hybrid JSON/JSONP renderer. JSONP is useful for making cross-domain AJAX |
| | | requests. |
| | | |
| | |
| | | |
| | | .. method:: pop_flash(queue='') |
| | | |
| | | >>> request.session.flash('info message') |
| | | >>> request.session.pop_flash() |
| | | ['info message'] |
| | | .. testsetup:: |
| | | |
| | | from pyramid import testing |
| | | request = testing.DummyRequest() |
| | | |
| | | .. doctest:: |
| | | |
| | | >>> request.session.flash('info message') |
| | | >>> request.session.pop_flash() |
| | | ['info message'] |
| | | |
| | | Calling ``session.pop_flash()`` again like above without a corresponding call |
| | | to ``session.flash()`` will return an empty list, because the queue has already |
| | | been popped. |
| | | |
| | | >>> request.session.flash('info message') |
| | | >>> request.session.pop_flash() |
| | | ['info message'] |
| | | >>> request.session.pop_flash() |
| | | [] |
| | | .. doctest:: |
| | | |
| | | >>> request.session.flash('info message') |
| | | >>> request.session.pop_flash() |
| | | ['info message'] |
| | | >>> request.session.pop_flash() |
| | | [] |
| | | |
| | | .. index:: |
| | | single: session.peek_flash |
| | |
| | | |
| | | .. method:: peek_flash(queue='') |
| | | |
| | | >>> request.session.flash('info message') |
| | | >>> request.session.peek_flash() |
| | | ['info message'] |
| | | >>> request.session.peek_flash() |
| | | ['info message'] |
| | | >>> request.session.pop_flash() |
| | | ['info message'] |
| | | >>> request.session.peek_flash() |
| | | [] |
| | | .. doctest:: |
| | | |
| | | >>> request.session.flash('info message') |
| | | >>> request.session.peek_flash() |
| | | ['info message'] |
| | | >>> request.session.peek_flash() |
| | | ['info message'] |
| | | >>> request.session.pop_flash() |
| | | ['info message'] |
| | | >>> request.session.peek_flash() |
| | | [] |
| | | |
| | | .. index:: |
| | | single: preventing cross-site request forgery attacks |
| | |
| | | --------------------------------------------- |
| | | |
| | | `Cross-site request forgery |
| | | <http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ attacks are a |
| | | <https://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ attacks are a |
| | | phenomenon whereby a user who is logged in to your website might inadvertantly |
| | | load a URL because it is linked from, or embedded in, an attacker's website. |
| | | If the URL is one that may modify or delete data, the consequences can be dire. |
| | |
| | | |
| | | .. code-block:: python |
| | | |
| | | from pyramid.session import check_csrf_token |
| | | from pyramid.session import check_csrf_token |
| | | |
| | | def myview(request): |
| | | # Require CSRF Token |
| | | check_csrf_token(request) |
| | | def myview(request): |
| | | # Require CSRF Token |
| | | check_csrf_token(request) |
| | | |
| | | # ... |
| | | # ... |
| | | |
| | | .. _auto_csrf_checking: |
| | | |
| | |
| | | :app:`Pyramid` supports automatically checking CSRF tokens on requests with an |
| | | unsafe method as defined by RFC2616. Any other request may be checked manually. |
| | | This feature can be turned on globally for an application using the |
| | | ``pyramid.require_default_csrf`` setting. |
| | | :meth:`pyramid.config.Configurator.set_default_csrf_options` directive. |
| | | For example: |
| | | |
| | | If the ``pyramid.required_default_csrf`` setting is a :term:`truthy string` or |
| | | ``True`` then the default CSRF token parameter will be ``csrf_token``. If a |
| | | different token is desired, it may be passed as the value. Finally, a |
| | | :term:`falsey string` or ``False`` will turn off automatic CSRF checking |
| | | globally on every request. |
| | | .. code-block:: python |
| | | |
| | | No matter what, CSRF checking may be explicitly enabled or disabled on a |
| | | per-view basis using the ``require_csrf`` view option. This option is of the |
| | | same format as the ``pyramid.require_default_csrf`` setting, accepting strings |
| | | or boolean values. |
| | | from pyramid.config import Configurator |
| | | |
| | | If ``require_csrf`` is ``True`` but does not explicitly define a token to |
| | | check, then the token name is pulled from whatever was set in the |
| | | ``pyramid.require_default_csrf`` setting. Finally, if that setting does not |
| | | explicitly define a token, then ``csrf_token`` is the token required. This token |
| | | name will be required in ``request.POST`` which is the submitted form body. |
| | | config = Configurator() |
| | | config.set_default_csrf_options(require_csrf=True) |
| | | |
| | | It is always possible to pass the token in the ``X-CSRF-Token`` header as well. |
| | | There is currently no way to define an alternate name for this header without |
| | | performing CSRF checking manually. |
| | | CSRF checking may be explicitly enabled or disabled on a per-view basis using |
| | | the ``require_csrf`` view option. A value of ``True`` or ``False`` will |
| | | override the default set by ``set_default_csrf_options``. For example: |
| | | |
| | | In addition to token based CSRF checks, the automatic CSRF checking will also |
| | | check the referrer of the request to ensure that it matches one of the trusted |
| | | origins. By default the only trusted origin is the current host, however |
| | | additional origins may be configured by setting |
| | | .. code-block:: python |
| | | |
| | | @view_config(route_name='hello', require_csrf=False) |
| | | def myview(request): |
| | | # ... |
| | | |
| | | When CSRF checking is active, the token and header used to find the |
| | | supplied CSRF token will be ``csrf_token`` and ``X-CSRF-Token``, respectively, |
| | | unless otherwise overridden by ``set_default_csrf_options``. The token is |
| | | checked against the value in ``request.POST`` which is the submitted form body. |
| | | If this value is not present, then the header will be checked. |
| | | |
| | | In addition to token based CSRF checks, if the request is using HTTPS then the |
| | | automatic CSRF checking will also check the referrer of the request to ensure |
| | | that it matches one of the trusted origins. By default the only trusted origin |
| | | is the current host, however additional origins may be configured by setting |
| | | ``pyramid.csrf_trusted_origins`` to a list of domain names (and ports if they |
| | | are non standard). If a host in the list of domains starts with a ``.`` then |
| | | that will allow all subdomains as well as the domain without the ``.``. |
| | | |
| | | If CSRF checks fail then a :class:`pyramid.exceptions.BadCSRFToken` exception |
| | | will be raised. This exception may be caught and handled by an |
| | | :term:`exception view` but, by default, will result in a ``400 Bad Request`` |
| | | response being sent to the client. |
| | | If CSRF checks fail then a :class:`pyramid.exceptions.BadCSRFToken` or |
| | | :class:`pyramid.exceptions.BadCSRFOrigin` exception will be raised. This |
| | | exception may be caught and handled by an :term:`exception view` but, by |
| | | default, will result in a ``400 Bad Request`` response being sent to the |
| | | client. |
| | | |
| | | Checking CSRF Tokens with a View Predicate |
| | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | |
| | | .. _pyramid_chameleon: |
| | | http://docs.pylonsproject.org/projects/pyramid-chameleon/en/latest/ |
| | | |
| | | .. _Jinja2: http://jinja.pocoo.org/docs/ |
| | | .. _Jinja2: http://jinja.pocoo.org/docs/dev/ |
| | | .. _pyramid_jinja2: |
| | | http://docs.pylonsproject.org/projects/pyramid-jinja2/en/latest/ |
| | | |
| | |
| | | |
| | | The ``-Wd`` argument tells Python to print deprecation warnings to the console. |
| | | See `the Python -W flag documentation |
| | | <http://docs.python.org/using/cmdline.html#cmdoption-W>`_ for more information. |
| | | <https://docs.python.org/2/using/cmdline.html#cmdoption-W>`_ for more |
| | | information. |
| | | |
| | | As your tests run, deprecation warnings will be printed to the console |
| | | explaining the deprecation and providing instructions about how to prevent the |
| | |
| | | as explained in :ref:`testing_under_new_release`. |
| | | |
| | | See `the PYTHONWARNINGS environment variable documentation |
| | | <http://docs.python.org/using/cmdline.html#envvar-PYTHONWARNINGS>`_ or `the |
| | | <https://docs.python.org/2/using/cmdline.html#envvar-PYTHONWARNINGS>`_ or `the |
| | | Python -W flag documentation |
| | | <http://docs.python.org/using/cmdline.html#cmdoption-W>`_ for more information. |
| | | <https://docs.python.org/2/using/cmdline.html#cmdoption-W>`_ for more |
| | | information. |
| | | |
| | | Upgrading to the very latest Pyramid release |
| | | -------------------------------------------- |
| | |
| | | But this will either cause an error at startup time or it won't match properly. |
| | | You'll want to use a Unicode value as the pattern instead rather than raw |
| | | bytestring escapes. You can use a high-order Unicode value as the pattern by |
| | | using `Python source file encoding <http://www.python.org/dev/peps/pep-0263/>`_ |
| | | plus the "real" character in the Unicode pattern in the source, like so: |
| | | using `Python source file encoding |
| | | <https://www.python.org/dev/peps/pep-0263/>`_ plus the "real" character in the |
| | | Unicode pattern in the source, like so: |
| | | |
| | | .. code-block:: text |
| | | |
| | |
| | | __text__ = 'my custom class predicate' |
| | | |
| | | If a predicate is a method, you'll need to assign it after method declaration |
| | | (see `PEP 232 <http://www.python.org/dev/peps/pep-0232/>`_). |
| | | (see `PEP 232 <https://www.python.org/dev/peps/pep-0232/>`_). |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | :ref:`request_module` API documentation. |
| | | |
| | | WebOb provides objects for HTTP requests and responses. Specifically it does |
| | | this by wrapping the `WSGI <http://wsgi.org>`_ request environment and response |
| | | status, header list, and app_iter (body) values. |
| | | this by wrapping the `WSGI <http://wsgi.readthedocs.org/en/latest/>`_ request |
| | | environment and response status, header list, and app_iter (body) values. |
| | | |
| | | WebOb request and response objects provide many conveniences for parsing WSGI |
| | | requests and forming WSGI responses. WebOb is a nice way to represent "raw" |
| | |
| | | ~~~~~~~ |
| | | |
| | | The request object is a wrapper around the `WSGI environ dictionary |
| | | <http://www.python.org/dev/peps/pep-0333/#environ-variables>`_. This |
| | | <https://www.python.org/dev/peps/pep-0333/#environ-variables>`_. This |
| | | dictionary contains keys for each header, keys that describe the request |
| | | (including the path and query string), a file-like object for the request body, |
| | | and a variety of custom keys. You can always access the environ with |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test --cov=hello_world --cov-report=term-missing hello_world/tests.py |
| | | $ $VENV/bin/py.test --cov --cov-report=term-missing |
| | | |
| | | This yields the following output. |
| | | |
| | |
| | | .. _qtut_forms: |
| | | |
| | | ==================================== |
| | | 18: Forms and Validation With Deform |
| | | 18: Forms and Validation with Deform |
| | | ==================================== |
| | | |
| | | Schema-driven, autogenerated forms with validation. |
| | |
| | | :ref:`Deform <deform:overview>` is one such library. In this step, we introduce |
| | | Deform for our forms. This also gives us :ref:`Colander <colander:overview>` |
| | | for schemas and validation. |
| | | |
| | | Deform uses styling from Twitter Bootstrap and advanced widgets from popular |
| | | JavaScript projects. |
| | | |
| | | |
| | | Objectives |
| | |
| | | |
| | | .. code-block:: text |
| | | |
| | | └── ~ |
| | | └── projects |
| | | └── quick_tutorial |
| | | ├── env |
| | | └── step_one |
| | | ├── intro |
| | | │ ├── __init__.py |
| | | │ └── app.py |
| | | └── setup.py |
| | | `── ~ |
| | | `── projects |
| | | `── quick_tutorial |
| | | │── env |
| | | `── step_one |
| | | │── intro |
| | | │ │── __init__.py |
| | | │ `── app.py |
| | | `── setup.py |
| | | |
| | | For Linux, the commands to do so are as follows: |
| | | |
| | |
| | | .. code-block:: text |
| | | |
| | | quick_tutorial |
| | | ├── env |
| | | └── request_response |
| | | ├── tutorial |
| | | │ ├── __init__.py |
| | | │ ├── tests.py |
| | | │ └── views.py |
| | | ├── development.ini |
| | | └── setup.py |
| | | │── env |
| | | `── request_response |
| | | `── tutorial |
| | | │ │── __init__.py |
| | | │ │── tests.py |
| | | │ `── views.py |
| | | │── development.ini |
| | | `── setup.py |
| | | |
| | | Each of the first-level directories (e.g., ``request_response``) is a *Python |
| | | project* (except as noted for the ``hello_world`` step). The ``tutorial`` |
| | |
| | | ``mod_wsgi``. If you have experience with :app:`Pyramid` and ``mod_wsgi`` |
| | | on Windows systems, please help us document this experience by submitting |
| | | documentation to the `Pylons-devel maillist |
| | | <http://groups.google.com/group/pylons-devel>`_. |
| | | <https://groups.google.com/forum/#!forum/pylons-devel>`_. |
| | | |
| | | #. The tutorial assumes you have Apache already installed on your |
| | | system. If you do not, install Apache 2.X for your platform in |
| | |
| | | |
| | | #. Once you have Apache installed, install ``mod_wsgi``. Use the |
| | | (excellent) `installation instructions |
| | | <http://code.google.com/p/modwsgi/wiki/InstallationInstructions>`_ |
| | | <https://code.google.com/archive/p/modwsgi/wikis/InstallationInstructions.wiki>`_ |
| | | for your platform into your system's Apache installation. |
| | | |
| | | #. Create a :term:`virtual environment` which we'll use to install our |
| | |
| | | |
| | | #. Install :app:`Pyramid` into the newly created virtual environment: |
| | | |
| | | .. code-block:: text |
| | | .. parsed-literal:: |
| | | |
| | | $ cd ~/modwsgi/env |
| | | $ $VENV/bin/pip install pyramid |
| | | |
| | | $ $VENV/bin/pip install "pyramid==\ |release|\ " |
| | | |
| | | #. Create and install your :app:`Pyramid` application. For the purposes of |
| | | this tutorial, we'll just be using the ``pyramid_starter`` application as |
| | | a baseline application. Substitute your existing :app:`Pyramid` |
| | |
| | | #. Visit ``http://localhost/myapp`` in a browser. You should see the |
| | | sample application rendered in your browser. |
| | | |
| | | :term:`mod_wsgi` has many knobs and a great variety of deployment |
| | | modes. This is just one representation of how you might use it to |
| | | serve up a :app:`Pyramid` application. See the `mod_wsgi |
| | | configuration documentation |
| | | <http://code.google.com/p/modwsgi/wiki/ConfigurationGuidelines>`_ for |
| | | more in-depth configuration information. |
| | | :term:`mod_wsgi` has many knobs and a great variety of deployment modes. This |
| | | is just one representation of how you might use it to serve up a :app:`Pyramid` |
| | | application. See the `mod_wsgi configuration documentation |
| | | <https://code.google.com/archive/p/modwsgi/wikis/ConfigurationGuidelines.wiki>`_ |
| | | for more in-depth configuration information. |
| | |
| | | ``tutorial-0.0.tar.gz``. You can send this file to your friends to show them |
| | | your cool new application. They should be able to install it by pointing the |
| | | ``pip install .`` command directly at it. Or you can upload it to `PyPI |
| | | <http://pypi.python.org>`_ and share it with the rest of the world, where it |
| | | can be downloaded via ``pip install`` remotely like any other package people |
| | | <https://pypi.python.org/pypi>`_ and share it with the rest of the world, where |
| | | it can be downloaded via ``pip install`` remotely like any other package people |
| | | download from PyPI. |
| | |
| | | On UNIX |
| | | ^^^^^^^ |
| | | |
| | | .. code-block:: bash |
| | | .. parsed-literal:: |
| | | |
| | | $ $VENV/bin/pip install pyramid |
| | | $ $VENV/bin/pip install "pyramid==\ |release|\ " |
| | | |
| | | On Windows |
| | | ^^^^^^^^^^ |
| | | |
| | | .. code-block:: doscon |
| | | .. parsed-literal:: |
| | | |
| | | c:\> %VENV%\Scripts\pip install pyramid |
| | | c:\\> %VENV%\\Scripts\\pip install "pyramid==\ |release|\ " |
| | | |
| | | |
| | | Change directory to your virtual Python environment |
| | | --------------------------------------------------- |
| | |
| | | .. code-block:: doscon |
| | | |
| | | c:\> cd pyramidtut |
| | | |
| | | |
| | | .. _making_a_project: |
| | | |
| | |
| | | zc.lockfile-1.1.0 zdaemon-4.1.0 zodbpickle-0.6.0 zodburi-2.0 |
| | | |
| | | |
| | | .. _install-testing-requirements_zodb: |
| | | .. _install-testing-requirements-zodb: |
| | | |
| | | Install testing requirements |
| | | ---------------------------- |
| | |
| | | ------------- |
| | | |
| | | After you've installed the project in development mode as well as the testing |
| | | requirements, you may run the tests for the project. |
| | | requirements, you may run the tests for the project. The following commands |
| | | provide options to py.test that specify the module for which its tests shall be |
| | | run, and to run py.test in quiet mode. |
| | | |
| | | On UNIX |
| | | ^^^^^^^ |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | $ $VENV/bin/py.test -q |
| | | |
| | | On Windows |
| | | ^^^^^^^^^^ |
| | | |
| | | .. code-block:: doscon |
| | | |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test tutorial\tests.py -q |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test -q |
| | | |
| | | For a successful test run, you should see output that ends like this: |
| | | |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test --cov=tutorial --cov-report=term-missing tutorial/tests.py |
| | | $ $VENV/bin/py.test --cov --cov-report=term-missing |
| | | |
| | | On Windows |
| | | ^^^^^^^^^^ |
| | | |
| | | .. code-block:: doscon |
| | | |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test --cov=tutorial \ |
| | | --cov-report=term-missing tutorial\tests.py |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test --cov \ |
| | | --cov-report=term-missing |
| | | |
| | | If successful, you will see output something like this: |
| | | |
| | |
| | | ===================== 1 passed in 0.31 seconds ====================== |
| | | |
| | | Our package doesn't quite have 100% test coverage. |
| | | |
| | | |
| | | .. _test_and_coverage_scaffold_defaults_zodb: |
| | | |
| | | Test and coverage scaffold defaults |
| | | ----------------------------------- |
| | | |
| | | Scaffolds include configuration defaults for ``py.test`` and test coverage. |
| | | These configuration files are ``pytest.ini`` and ``.coveragerc``, located at |
| | | the root of your package. Without these defaults, we would need to specify the |
| | | path to the module on which we want to run tests and coverage. |
| | | |
| | | On UNIX |
| | | ^^^^^^^ |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test --cov=tutorial tutorial/tests.py -q |
| | | |
| | | On Windows |
| | | ^^^^^^^^^^ |
| | | |
| | | .. code-block:: doscon |
| | | |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test --cov=tutorial \ |
| | | --cov-report=term-missing tutorial\tests.py -q |
| | | |
| | | py.test follows :ref:`conventions for Python test discovery |
| | | <pytest:test discovery>`, and the configuration defaults from the scaffold |
| | | tell ``py.test`` where to find the module on which we want to run tests and |
| | | coverage. |
| | | |
| | | .. seealso:: See py.test's documentation for :ref:`pytest:usage` or invoke |
| | | ``py.test -h`` to see its full set of options. |
| | | |
| | | |
| | | .. _wiki-start-the-application: |
| | |
| | | |
| | | .. code-block:: text |
| | | |
| | | Starting subprocess with file monitor |
| | | Starting server in PID 95736. |
| | | serving on http://127.0.0.1:6543 |
| | | Starting subprocess with file monitor |
| | | Starting server in PID 82349. |
| | | serving on http://127.0.0.1:6543 |
| | | |
| | | This means the server is ready to accept requests. |
| | | |
| | |
| | | |
| | | - You are willing to use :term:`traversal` to map URLs to code. |
| | | |
| | | - You want to use pyramid_zodbconn_, pyramid_tm_, and the transaction_ packages |
| | | to manage connections and transactions with :term:`ZODB`. |
| | | |
| | | - You want to use pyramid_chameleon_ to render your templates. Different |
| | | templating engines can be used, but we had to choose one to make this |
| | | tutorial. See :ref:`available_template_system_bindings` for some options. |
| | | |
| | | .. note:: |
| | | |
| | | :app:`Pyramid` supports any persistent storage mechanism (e.g., a SQL |
| | | database or filesystem files). It also supports an additional |
| | | mechanism to map URLs to code (:term:`URL dispatch`). However, for the |
| | | purposes of this tutorial, we'll only be using traversal and ZODB. |
| | | :app:`Pyramid` supports any persistent storage mechanism (e.g., an SQL |
| | | database or filesystem files). It also supports an additional mechanism to |
| | | map URLs to code (:term:`URL dispatch`). However, for the purposes of this |
| | | tutorial, we'll only be using :term:`traversal` and :term:`ZODB`. |
| | | |
| | | .. _pyramid_chameleon: |
| | | http://docs.pylonsproject.org/projects/pyramid-chameleon/en/latest/ |
| | | |
| | | .. _pyramid_tm: |
| | | http://docs.pylonsproject.org/projects/pyramid-tm/en/latest/ |
| | | |
| | | .. _pyramid_zodbconn: |
| | | http://docs.pylonsproject.org/projects/pyramid-zodbconn/en/latest/ |
| | | |
| | | .. _transaction: |
| | | http://zodb.readthedocs.org/en/latest/transactions.html |
| | |
| | | ================= |
| | | |
| | | We can run these tests by using ``py.test`` similarly to how we did in |
| | | :ref:`running_tests`. Our testing dependencies have already been satisfied, |
| | | courtesy of the scaffold, so we can jump right to running tests. |
| | | :ref:`running_tests`. Courtesy of the scaffold, our testing dependencies have |
| | | already been satisfied and ``py.test`` and coverage have already been |
| | | configured, so we can jump right to running tests. |
| | | |
| | | On UNIX: |
| | | |
| | | .. code-block:: text |
| | | |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | $ $VENV/bin/py.test -q |
| | | |
| | | On Windows: |
| | | |
| | | .. code-block:: text |
| | | |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test tutorial/tests.py -q |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test -q |
| | | |
| | | The expected result should look like the following: |
| | | |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | 2016-04-09 02:49:51,711 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 |
| | | 2016-04-09 02:49:51,711 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () |
| | | 2016-04-09 02:49:51,712 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 |
| | | 2016-04-09 02:49:51,712 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () |
| | | 2016-04-09 02:49:51,713 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("pages") |
| | | 2016-04-09 02:49:51,714 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-04-09 02:49:51,714 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("users") |
| | | 2016-04-09 02:49:51,714 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-04-09 02:49:51,715 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] |
| | | 2016-05-22 04:12:09,226 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 |
| | | 2016-05-22 04:12:09,226 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () |
| | | 2016-05-22 04:12:09,226 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 |
| | | 2016-05-22 04:12:09,227 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () |
| | | 2016-05-22 04:12:09,227 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("users") |
| | | 2016-05-22 04:12:09,227 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-05-22 04:12:09,228 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("pages") |
| | | 2016-05-22 04:12:09,228 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-05-22 04:12:09,229 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] |
| | | CREATE TABLE users ( |
| | | id INTEGER NOT NULL, |
| | | name TEXT NOT NULL, |
| | | role TEXT NOT NULL, |
| | | password_hash TEXT, |
| | | CONSTRAINT pk_users PRIMARY KEY (id), |
| | | CONSTRAINT uq_users_name UNIQUE (name) |
| | | id INTEGER NOT NULL, |
| | | name TEXT NOT NULL, |
| | | role TEXT NOT NULL, |
| | | password_hash TEXT, |
| | | CONSTRAINT pk_users PRIMARY KEY (id), |
| | | CONSTRAINT uq_users_name UNIQUE (name) |
| | | ) |
| | | |
| | | |
| | | 2016-04-09 02:49:51,715 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-04-09 02:49:51,716 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | 2016-04-09 02:49:51,716 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] |
| | | 2016-05-22 04:12:09,229 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-05-22 04:12:09,230 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | 2016-05-22 04:12:09,230 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] |
| | | CREATE TABLE pages ( |
| | | id INTEGER NOT NULL, |
| | | name TEXT NOT NULL, |
| | | data INTEGER NOT NULL, |
| | | creator_id INTEGER NOT NULL, |
| | | CONSTRAINT pk_pages PRIMARY KEY (id), |
| | | CONSTRAINT uq_pages_name UNIQUE (name), |
| | | CONSTRAINT fk_pages_creator_id_users FOREIGN KEY(creator_id) REFERENCES users (id) |
| | | id INTEGER NOT NULL, |
| | | name TEXT NOT NULL, |
| | | data TEXT NOT NULL, |
| | | creator_id INTEGER NOT NULL, |
| | | CONSTRAINT pk_pages PRIMARY KEY (id), |
| | | CONSTRAINT uq_pages_name UNIQUE (name), |
| | | CONSTRAINT fk_pages_creator_id_users FOREIGN KEY(creator_id) REFERENCES users (id) |
| | | ) |
| | | |
| | | |
| | | 2016-04-09 02:49:51,716 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-04-09 02:49:51,717 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | 2016-04-09 02:49:52,256 INFO [sqlalchemy.engine.base.Engine:646][MainThread] BEGIN (implicit) |
| | | 2016-04-09 02:49:52,257 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) |
| | | 2016-04-09 02:49:52,257 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('editor', 'editor', b'$2b$12$APUPJvI/kKxrbQPyQehkR.ggoOM6fFYCZ07SFCkWGltl1wJsKB98y') |
| | | 2016-04-09 02:49:52,258 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) |
| | | 2016-04-09 02:49:52,258 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('basic', 'basic', b'$2b$12$GeFnypuQpZyxZLH.sN0akOrPdZMcQjqVTCim67u6f89lOFH/0ddc6') |
| | | 2016-04-09 02:49:52,259 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO pages (name, data, creator_id) VALUES (?, ?, ?) |
| | | 2016-04-09 02:49:52,259 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('FrontPage', 'This is the front page', 1) |
| | | 2016-04-09 02:49:52,259 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | 2016-05-22 04:12:09,231 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-05-22 04:12:09,231 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | 2016-05-22 04:12:09,782 INFO [sqlalchemy.engine.base.Engine:646][MainThread] BEGIN (implicit) |
| | | 2016-05-22 04:12:09,783 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) |
| | | 2016-05-22 04:12:09,784 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('editor', 'editor', b'$2b$12$K/WLVKRl5fMAb6UM58ueTetXlE3rlc5cRK5zFPimK598scXBR/xWC') |
| | | 2016-05-22 04:12:09,784 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) |
| | | 2016-05-22 04:12:09,784 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('basic', 'basic', b'$2b$12$JfwLyCJGv3t.RTSmIrh3B.FKXRT9FevkAqafWdK5oq7Hl4mgAQORe') |
| | | 2016-05-22 04:12:09,785 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO pages (name, data, creator_id) VALUES (?, ?, ?) |
| | | 2016-05-22 04:12:09,785 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('FrontPage', 'This is the front page', 1) |
| | | 2016-05-22 04:12:09,786 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | |
| | | |
| | | View the application in a browser |
| | |
| | | ``tutorial-0.0.tar.gz``. You can send this file to your friends to show them |
| | | your cool new application. They should be able to install it by pointing the |
| | | ``easy_install`` command directly at it. Or you can upload it to `PyPI |
| | | <http://pypi.python.org>`_ and share it with the rest of the world, where it |
| | | can be downloaded via ``easy_install`` remotely like any other package people |
| | | download from PyPI. |
| | | <https://pypi.python.org/pypi>`_ and share it with the rest of the world, where |
| | | it can be downloaded via ``easy_install`` remotely like any other package |
| | | people download from PyPI. |
| | |
| | | On UNIX |
| | | ^^^^^^^ |
| | | |
| | | .. code-block:: bash |
| | | .. parsed-literal:: |
| | | |
| | | $ $VENV/bin/pip install pyramid |
| | | $ $VENV/bin/pip install "pyramid==\ |release|\ " |
| | | |
| | | On Windows |
| | | ^^^^^^^^^^ |
| | | |
| | | .. code-block:: doscon |
| | | .. parsed-literal:: |
| | | |
| | | c:\> %VENV%\Scripts\pip install pyramid |
| | | c:\\> %VENV%\\Scripts\\pip install "pyramid==\ |release|\ " |
| | | |
| | | |
| | | Install SQLite3 and its development packages |
| | |
| | | ------------- |
| | | |
| | | After you've installed the project in development mode as well as the testing |
| | | requirements, you may run the tests for the project. |
| | | requirements, you may run the tests for the project. The following commands |
| | | provide options to py.test that specify the module for which its tests shall be |
| | | run, and to run py.test in quiet mode. |
| | | |
| | | On UNIX |
| | | ^^^^^^^ |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | $ $VENV/bin/py.test -q |
| | | |
| | | On Windows |
| | | ^^^^^^^^^^ |
| | | |
| | | .. code-block:: doscon |
| | | |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test tutorial\tests.py -q |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test -q |
| | | |
| | | For a successful test run, you should see output that ends like this: |
| | | |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test --cov=tutorial --cov-report=term-missing tutorial/tests.py |
| | | $ $VENV/bin/py.test --cov --cov-report=term-missing |
| | | |
| | | On Windows |
| | | ^^^^^^^^^^ |
| | | |
| | | .. code-block:: doscon |
| | | |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test --cov=tutorial \ |
| | | --cov-report=term-missing tutorial\tests.py |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test --cov \ |
| | | --cov-report=term-missing |
| | | |
| | | If successful, you will see output something like this: |
| | | |
| | |
| | | tutorial/models/__init__.py 22 0 100% |
| | | tutorial/models/meta.py 5 0 100% |
| | | tutorial/models/mymodel.py 8 0 100% |
| | | tutorial/routes.py 3 3 0% 1-3 |
| | | tutorial/routes.py 3 2 33% 2-3 |
| | | tutorial/scripts/__init__.py 0 0 100% |
| | | tutorial/scripts/initializedb.py 26 26 0% 1-45 |
| | | tutorial/tests.py 39 0 100% |
| | | tutorial/scripts/initializedb.py 26 16 38% 22-25, 29-45 |
| | | tutorial/views/__init__.py 0 0 100% |
| | | tutorial/views/default.py 12 0 100% |
| | | tutorial/views/notfound.py 4 4 0% 1-7 |
| | | tutorial/views/notfound.py 4 2 50% 6-7 |
| | | ---------------------------------------------------------------- |
| | | TOTAL 127 39 69% |
| | | |
| | | TOTAL 88 26 70% |
| | | ===================== 2 passed in 0.57 seconds ====================== |
| | | |
| | | Our package doesn't quite have 100% test coverage. |
| | | |
| | | |
| | | .. _test_and_coverage_scaffold_defaults_sql: |
| | | |
| | | Test and coverage scaffold defaults |
| | | ----------------------------------- |
| | | |
| | | Scaffolds include configuration defaults for ``py.test`` and test coverage. |
| | | These configuration files are ``pytest.ini`` and ``.coveragerc``, located at |
| | | the root of your package. Without these defaults, we would need to specify the |
| | | path to the module on which we want to run tests and coverage. |
| | | |
| | | On UNIX |
| | | ^^^^^^^ |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test --cov=tutorial tutorial/tests.py -q |
| | | |
| | | On Windows |
| | | ^^^^^^^^^^ |
| | | |
| | | .. code-block:: doscon |
| | | |
| | | c:\pyramidtut\tutorial> %VENV%\Scripts\py.test --cov=tutorial \ |
| | | --cov-report=term-missing tutorial\tests.py -q |
| | | |
| | | py.test follows :ref:`conventions for Python test discovery |
| | | <pytest:test discovery>`, and the configuration defaults from the scaffold |
| | | tell ``py.test`` where to find the module on which we want to run tests and |
| | | coverage. |
| | | |
| | | .. seealso:: See py.test's documentation for :ref:`pytest:usage` or invoke |
| | | ``py.test -h`` to see its full set of options. |
| | | |
| | | |
| | | .. _initialize_db_wiki2: |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | 2016-04-09 00:53:37,801 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 |
| | | 2016-04-09 00:53:37,801 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () |
| | | 2016-04-09 00:53:37,802 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 |
| | | 2016-04-09 00:53:37,802 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () |
| | | 2016-04-09 00:53:37,802 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("models") |
| | | 2016-04-09 00:53:37,803 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-04-09 00:53:37,803 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] |
| | | 2016-05-22 04:03:28,888 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 |
| | | 2016-05-22 04:03:28,888 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () |
| | | 2016-05-22 04:03:28,888 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 |
| | | 2016-05-22 04:03:28,889 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () |
| | | 2016-05-22 04:03:28,890 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("models") |
| | | 2016-05-22 04:03:28,890 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-05-22 04:03:28,892 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] |
| | | CREATE TABLE models ( |
| | | id INTEGER NOT NULL, |
| | | name TEXT, |
| | | value INTEGER, |
| | | CONSTRAINT pk_models PRIMARY KEY (id) |
| | | id INTEGER NOT NULL, |
| | | name TEXT, |
| | | value INTEGER, |
| | | CONSTRAINT pk_models PRIMARY KEY (id) |
| | | ) |
| | | |
| | | |
| | | 2016-04-09 00:53:37,803 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-04-09 00:53:37,804 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | 2016-04-09 00:53:37,805 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] CREATE UNIQUE INDEX my_index ON models (name) |
| | | 2016-04-09 00:53:37,805 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-04-09 00:53:37,806 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | 2016-04-09 00:53:37,807 INFO [sqlalchemy.engine.base.Engine:646][MainThread] BEGIN (implicit) |
| | | 2016-04-09 00:53:37,808 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO models (name, value) VALUES (?, ?) |
| | | 2016-04-09 00:53:37,808 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('one', 1) |
| | | 2016-04-09 00:53:37,809 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | 2016-05-22 04:03:28,892 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-05-22 04:03:28,893 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | 2016-05-22 04:03:28,893 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] CREATE UNIQUE INDEX my_index ON models (name) |
| | | 2016-05-22 04:03:28,893 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () |
| | | 2016-05-22 04:03:28,894 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | 2016-05-22 04:03:28,896 INFO [sqlalchemy.engine.base.Engine:646][MainThread] BEGIN (implicit) |
| | | 2016-05-22 04:03:28,897 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO models (name, value) VALUES (?, ?) |
| | | 2016-05-22 04:03:28,897 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('one', 1) |
| | | 2016-05-22 04:03:28,898 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT |
| | | |
| | | Success! You should now have a ``tutorial.sqlite`` file in your current |
| | | working directory. This is an SQLite database with a single table defined in it |
| | |
| | | Your OS firewall, if any, may pop up a dialog asking for authorization |
| | | to allow python to accept incoming network connections. |
| | | |
| | | If successful, you will see something like this on your console:: |
| | | If successful, you will see something like this on your console: |
| | | |
| | | .. code-block:: text |
| | | |
| | | Starting subprocess with file monitor |
| | | Starting server in PID 82349. |
| | |
| | | Visit the application in a browser |
| | | ---------------------------------- |
| | | |
| | | In a browser, visit http://localhost:6543/. You will see the generated |
| | | In a browser, visit http://localhost:6543/. You will see the generated |
| | | application's default page. |
| | | |
| | | One thing you'll notice is the "debug toolbar" icon on right hand side of the |
| | |
| | | :app:`Pyramid` supports any persistent storage mechanism (e.g., object |
| | | database or filesystem files). It also supports an additional mechanism to |
| | | map URLs to code (:term:`traversal`). However, for the purposes of this |
| | | tutorial, we'll only be using URL dispatch and SQLAlchemy. |
| | | |
| | | .. _pyramid_jinja2: |
| | | http://docs.pylonsproject.org/projects/pyramid-jinja2/en/latest/ |
| | | |
| | | .. _pyramid_tm: |
| | | http://docs.pylonsproject.org/projects/pyramid-tm/en/latest/ |
| | | |
| | | .. _zope.sqlalchemy: |
| | | https://pypi.python.org/pypi/zope.sqlalchemy |
| | | |
| | | .. _transaction: |
| | | http://zodb.readthedocs.org/en/latest/transactions.html |
| | | tutorial, we'll only be using :term:`URL dispatch` and :term:`SQLAlchemy`. |
| | | |
| | | .. _pyramid_jinja2: |
| | | http://docs.pylonsproject.org/projects/pyramid-jinja2/en/latest/ |
| | |
| | | __tablename__ = 'pages' |
| | | id = Column(Integer, primary_key=True) |
| | | name = Column(Text, nullable=False, unique=True) |
| | | data = Column(Integer, nullable=False) |
| | | data = Column(Text, nullable=False) |
| | | |
| | | creator_id = Column(ForeignKey('users.id'), nullable=False) |
| | | creator = relationship('User', backref='created_pages') |
| | |
| | | |
| | | def check_password(self, pw): |
| | | if self.password_hash is not None: |
| | | expected_hash = self.password_hash |
| | | expected_hash = self.password_hash.encode('utf8') |
| | | actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) |
| | | return expected_hash == actual_hash |
| | | return False |
| | |
| | | __tablename__ = 'pages' |
| | | id = Column(Integer, primary_key=True) |
| | | name = Column(Text, nullable=False, unique=True) |
| | | data = Column(Integer, nullable=False) |
| | | data = Column(Text, nullable=False) |
| | | |
| | | creator_id = Column(ForeignKey('users.id'), nullable=False) |
| | | creator = relationship('User', backref='created_pages') |
| | |
| | | |
| | | def check_password(self, pw): |
| | | if self.password_hash is not None: |
| | | expected_hash = self.password_hash |
| | | expected_hash = self.password_hash.encode('utf8') |
| | | actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) |
| | | return expected_hash == actual_hash |
| | | return False |
| | |
| | | __tablename__ = 'pages' |
| | | id = Column(Integer, primary_key=True) |
| | | name = Column(Text, nullable=False, unique=True) |
| | | data = Column(Integer, nullable=False) |
| | | data = Column(Text, nullable=False) |
| | | |
| | | creator_id = Column(ForeignKey('users.id'), nullable=False) |
| | | creator = relationship('User', backref='created_pages') |
| | |
| | | |
| | | def check_password(self, pw): |
| | | if self.password_hash is not None: |
| | | expected_hash = self.password_hash |
| | | expected_hash = self.password_hash.encode('utf8') |
| | | actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) |
| | | return expected_hash == actual_hash |
| | | return False |
| | |
| | | __tablename__ = 'pages' |
| | | id = Column(Integer, primary_key=True) |
| | | name = Column(Text, nullable=False, unique=True) |
| | | data = Column(Integer, nullable=False) |
| | | data = Column(Text, nullable=False) |
| | | |
| | | creator_id = Column(ForeignKey('users.id'), nullable=False) |
| | | creator = relationship('User', backref='created_pages') |
| | |
| | | |
| | | def check_password(self, pw): |
| | | if self.password_hash is not None: |
| | | expected_hash = self.password_hash |
| | | expected_hash = self.password_hash.encode('utf8') |
| | | actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) |
| | | return expected_hash == actual_hash |
| | | return False |
| | |
| | | __tablename__ = 'pages' |
| | | id = Column(Integer, primary_key=True) |
| | | name = Column(Text, nullable=False, unique=True) |
| | | data = Column(Integer, nullable=False) |
| | | data = Column(Text, nullable=False) |
| | | |
| | | creator_id = Column(ForeignKey('users.id'), nullable=False) |
| | | creator = relationship('User', backref='created_pages') |
| | |
| | | |
| | | def check_password(self, pw): |
| | | if self.password_hash is not None: |
| | | expected_hash = self.password_hash |
| | | expected_hash = self.password_hash.encode('utf8') |
| | | actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) |
| | | return expected_hash == actual_hash |
| | | return False |
| | |
| | | csrf token in the query string of a request. Only headers and request bodies |
| | | are supported. See https://github.com/Pylons/pyramid/pull/2500 |
| | | |
| | | - A global permission set via |
| | | :meth:`pyramid.config.Configurator.set_default_permission` will no longer |
| | | affect exception views. A permission must be set explicitly on the view for |
| | | it to be enforced. See https://github.com/Pylons/pyramid/pull/2534 |
| | | |
| | | Feature Additions |
| | | ----------------- |
| | | |
| | |
| | | other stages of the pipeline such as the raw response from a view or prior |
| | | to security checks. See https://github.com/Pylons/pyramid/pull/2021 |
| | | |
| | | - Added a new setting, ``pyramid.require_default_csrf`` which may be used |
| | | to turn on CSRF checks globally for every request in the application. |
| | | This should be considered a good default for websites built on Pyramid. |
| | | It is possible to opt-out of CSRF checks on a per-view basis by setting |
| | | ``require_csrf=False`` on those views. |
| | | See :ref:`auto_csrf_checking` and |
| | | https://github.com/Pylons/pyramid/pull/2413 |
| | | |
| | | - Added a ``require_csrf`` view option which will enforce CSRF checks on |
| | | requests with an unsafe method as defined by RFC2616. If the CSRF check fails |
| | | a ``BadCSRFToken`` exception will be raised and may be caught by exception |
| | |
| | | instead of a catchable exception. See :ref:`auto_csrf_checking`, |
| | | https://github.com/Pylons/pyramid/pull/2413 and |
| | | https://github.com/Pylons/pyramid/pull/2500 |
| | | |
| | | - Added a new method, |
| | | :meth:`pyramid.config.Configurator.set_csrf_default_options`, |
| | | for configuring CSRF checks used by the ``require_csrf=True`` view option. |
| | | This method can be used to turn on CSRF checks globally for every view |
| | | in the application. This should be considered a good default for websites |
| | | built on Pyramid. It is possible to opt-out of CSRF checks on a per-view |
| | | basis by setting ``require_csrf=False`` on those views. |
| | | See :ref:`auto_csrf_checking` and |
| | | https://github.com/Pylons/pyramid/pull/2413 and |
| | | https://github.com/Pylons/pyramid/pull/2518 |
| | | |
| | | - Added an additional CSRF validation that checks the origin/referrer of a |
| | | request and makes sure it matches the current ``request.domain``. This |
| | |
| | | ``EXCVIEW`` tween where you may need more control over the request. |
| | | See https://github.com/Pylons/pyramid/pull/2393 |
| | | |
| | | - A global permission set via |
| | | :meth:`pyramid.config.Configurator.set_default_permission` will no longer |
| | | affect exception views. A permission must be set explicitly on the view for |
| | | it to be enforced. See https://github.com/Pylons/pyramid/pull/2534 |
| | | |
| | | - Allow a leading ``=`` on the key of the request param predicate. |
| | | For example, ``'=abc=1'`` is equivalent down to |
| | | ``request.params['=abc'] == '1'``. |
| | |
| | | :func:`pyramid.paster.setup_logging`. |
| | | See https://github.com/Pylons/pyramid/pull/2399 |
| | | |
| | | - The :attr:`pyramid.tweens.EXCVIEW` tween will now re-raise the original |
| | | exception if no exception view could be found to handle it. This allows |
| | | the exception to be handled upstream by another tween or middelware. |
| | | See https://github.com/Pylons/pyramid/pull/2567 |
| | | |
| | | Deprecations |
| | | ------------ |
| | | |
| | |
| | | import sys |
| | | import types |
| | | |
| | | if platform.system() == 'Windows': # pragma: no cover |
| | | WIN = True |
| | | else: # pragma: no cover |
| | | WIN = False |
| | | WIN = platform.system() == 'Windows' |
| | | |
| | | try: # pragma: no cover |
| | | import __pypy__ |
| | |
| | | input_ = raw_input |
| | | else: |
| | | input_ = input |
| | | |
| | | if PY2: |
| | | from inspect import getargspec |
| | | else: |
| | | from inspect import getfullargspec as getargspec |
| | | |
| | | if PY2: |
| | | from io import BytesIO as NativeIO |
| | |
| | | from zope.interface import implementer |
| | | |
| | | from pyramid.interfaces import ( |
| | | IAuthorizationPolicy, |
| | | IAuthenticationPolicy, |
| | | IDefaultCSRFOptions, |
| | | IDefaultPermission, |
| | | PHASE1_CONFIG, |
| | | PHASE2_CONFIG, |
| | | ) |
| | | |
| | | from pyramid.config.util import as_sorted_tuple |
| | | from pyramid.exceptions import ConfigurationError |
| | | from pyramid.util import action_method |
| | | |
| | |
| | | self.action(IDefaultPermission, register, order=PHASE1_CONFIG, |
| | | introspectables=(intr, perm_intr,)) |
| | | |
| | | |
| | | def add_permission(self, permission_name): |
| | | """ |
| | | A configurator directive which registers a free-standing |
| | |
| | | intr['value'] = permission_name |
| | | self.action(None, introspectables=(intr,)) |
| | | |
| | | @action_method |
| | | def set_default_csrf_options( |
| | | self, |
| | | require_csrf=True, |
| | | token='csrf_token', |
| | | header='X-CSRF-Token', |
| | | safe_methods=('GET', 'HEAD', 'OPTIONS', 'TRACE'), |
| | | ): |
| | | """ |
| | | Set the default CSRF options used by subsequent view registrations. |
| | | |
| | | ``require_csrf`` controls whether CSRF checks will be automatically |
| | | enabled on each view in the application. This value is used as the |
| | | fallback when ``require_csrf`` is left at the default of ``None`` on |
| | | :meth:`pyramid.config.Configurator.add_view`. |
| | | |
| | | ``token`` is the name of the CSRF token used in the body of the |
| | | request, accessed via ``request.POST[token]``. Default: ``csrf_token``. |
| | | |
| | | ``header`` is the name of the header containing the CSRF token, |
| | | accessed via ``request.headers[header]``. Default: ``X-CSRF-Token``. |
| | | |
| | | If ``token`` or ``header`` are set to ``None`` they will not be used |
| | | for checking CSRF tokens. |
| | | |
| | | ``safe_methods`` is an iterable of HTTP methods which are expected to |
| | | not contain side-effects as defined by RFC2616. Safe methods will |
| | | never be automatically checked for CSRF tokens. |
| | | Default: ``('GET', 'HEAD', 'OPTIONS', TRACE')``. |
| | | |
| | | """ |
| | | options = DefaultCSRFOptions(require_csrf, token, header, safe_methods) |
| | | def register(): |
| | | self.registry.registerUtility(options, IDefaultCSRFOptions) |
| | | intr = self.introspectable('default csrf view options', |
| | | None, |
| | | options, |
| | | 'default csrf view options') |
| | | intr['require_csrf'] = require_csrf |
| | | intr['token'] = token |
| | | intr['header'] = header |
| | | intr['safe_methods'] = as_sorted_tuple(safe_methods) |
| | | self.action(IDefaultCSRFOptions, register, order=PHASE1_CONFIG, |
| | | introspectables=(intr,)) |
| | | |
| | | @implementer(IDefaultCSRFOptions) |
| | | class DefaultCSRFOptions(object): |
| | | def __init__(self, require_csrf, token, header, safe_methods): |
| | | self.require_csrf = require_csrf |
| | | self.token = token |
| | | self.header = header |
| | | self.safe_methods = frozenset(safe_methods) |
| | |
| | | config_prevent_cachebust) |
| | | eff_prevent_cachebust = asbool(eget('PYRAMID_PREVENT_CACHEBUST', |
| | | config_prevent_cachebust)) |
| | | require_default_csrf = self.get('pyramid.require_default_csrf') |
| | | eff_require_default_csrf = require_default_csrf |
| | | csrf_trusted_origins = self.get("pyramid.csrf_trusted_origins", []) |
| | | eff_csrf_trusted_origins = csrf_trusted_origins |
| | | |
| | |
| | | 'default_locale_name':eff_locale_name, |
| | | 'prevent_http_cache':eff_prevent_http_cache, |
| | | 'prevent_cachebust':eff_prevent_cachebust, |
| | | 'require_default_csrf':eff_require_default_csrf, |
| | | 'csrf_trusted_origins':eff_csrf_trusted_origins, |
| | | |
| | | 'pyramid.debug_authorization': eff_debug_all or eff_debug_auth, |
| | |
| | | 'pyramid.default_locale_name':eff_locale_name, |
| | | 'pyramid.prevent_http_cache':eff_prevent_http_cache, |
| | | 'pyramid.prevent_cachebust':eff_prevent_cachebust, |
| | | 'pyramid.require_default_csrf':eff_require_default_csrf, |
| | | 'pyramid.csrf_trusted_origins':eff_csrf_trusted_origins, |
| | | } |
| | | |
| | |
| | | from zope.interface.interfaces import IInterface |
| | | |
| | | from pyramid.interfaces import ( |
| | | IDefaultPermission, |
| | | IException, |
| | | IExceptionViewClassifier, |
| | | IMultiView, |
| | |
| | | |
| | | .. versionadded:: 1.7 |
| | | |
| | | CSRF checks only affect POST requests. Any other request methods |
| | | will pass untouched. This option is used in combination with the |
| | | ``pyramid.require_default_csrf`` setting to control which |
| | | request parameters are checked for CSRF tokens. |
| | | A boolean option or ``None``. Default: ``None``. |
| | | |
| | | If this option is set to ``True`` then CSRF checks will be enabled |
| | | for requests to this view. The required token or header default to |
| | | ``csrf_token`` and ``X-CSRF-Token``, respectively. |
| | | |
| | | CSRF checks only affect "unsafe" methods as defined by RFC2616. By |
| | | default, these methods are anything except |
| | | ``GET``, ``HEAD``, ``OPTIONS``, and ``TRACE``. |
| | | |
| | | The defaults here may be overridden by |
| | | :meth:`pyramid.config.Configurator.set_default_csrf_options`. |
| | | |
| | | This feature requires a configured :term:`session factory`. |
| | | |
| | | If this option is set to ``True`` then CSRF checks will be enabled |
| | | for POST requests to this view. The required token will be whatever |
| | | was specified by the ``pyramid.require_default_csrf`` setting, or |
| | | will fallback to ``csrf_token``. |
| | | |
| | | If this option is set to a string then CSRF checks will be enabled |
| | | and it will be used as the required token regardless of the |
| | | ``pyramid.require_default_csrf`` setting. |
| | | |
| | | If this option is set to ``False`` then CSRF checks will be disabled |
| | | regardless of the ``pyramid.require_default_csrf`` setting. |
| | | regardless of the default ``require_csrf`` setting passed |
| | | to ``set_default_csrf_options``. |
| | | |
| | | See :ref:`auto_csrf_checking` for more information. |
| | | |
| | |
| | | registry=self.registry |
| | | ) |
| | | |
| | | if permission is None: |
| | | # intent: will be None if no default permission is registered |
| | | # (reg'd in phase 1) |
| | | permission = self.registry.queryUtility(IDefaultPermission) |
| | | |
| | | # added by discrim_func above during conflict resolving |
| | | preds = view_intr['predicates'] |
| | | order = view_intr['order'] |
| | |
| | | d = pyramid.viewderivers |
| | | derivers = [ |
| | | ('secured_view', d.secured_view), |
| | | ('csrf_view', d.csrf_view), |
| | | ('owrapped_view', d.owrapped_view), |
| | | ('http_cached_view', d.http_cached_view), |
| | | ('decorated_view', d.decorated_view), |
| | |
| | | over=VIEW, |
| | | ) |
| | | last = name |
| | | |
| | | # leave the csrf_view loosely coupled to the rest of the pipeline |
| | | # by ensuring nothing in the default pipeline depends on the order |
| | | # of the csrf_view |
| | | self.add_view_deriver( |
| | | d.csrf_view, |
| | | 'csrf_view', |
| | | under='secured_view', |
| | | over='owrapped_view', |
| | | ) |
| | | |
| | | def derive_view(self, view, attr=None, renderer=None): |
| | | """ |
| | |
| | | |
| | | .. versionadded:: 1.3 |
| | | """ |
| | | for arg in ('name', 'permission', 'context', 'for_', 'http_cache'): |
| | | for arg in ( |
| | | 'name', 'permission', 'context', 'for_', 'http_cache', |
| | | 'require_csrf', |
| | | ): |
| | | if arg in view_options: |
| | | raise ConfigurationError( |
| | | '%s may not be used as an argument to add_forbidden_view' |
| | |
| | | match_param=match_param, |
| | | route_name=route_name, |
| | | permission=NO_PERMISSION_REQUIRED, |
| | | require_csrf=False, |
| | | attr=attr, |
| | | renderer=renderer, |
| | | ) |
| | |
| | | .. versionchanged:: 1.6 |
| | | .. versionadded:: 1.3 |
| | | """ |
| | | for arg in ('name', 'permission', 'context', 'for_', 'http_cache'): |
| | | for arg in ( |
| | | 'name', 'permission', 'context', 'for_', 'http_cache', |
| | | 'require_csrf', |
| | | ): |
| | | if arg in view_options: |
| | | raise ConfigurationError( |
| | | '%s may not be used as an argument to add_notfound_view' |
| | |
| | | match_param=match_param, |
| | | route_name=route_name, |
| | | permission=NO_PERMISSION_REQUIRED, |
| | | require_csrf=False, |
| | | ) |
| | | settings.update(view_options) |
| | | if append_slash: |
| | |
| | | import functools |
| | | from functools import update_wrapper |
| | | |
| | | |
| | | class reify(object): |
| | |
| | | replacing the function it decorates with an instance variable. It is, in |
| | | Python parlance, a non-data descriptor. An example: |
| | | |
| | | .. code-block:: python |
| | | .. testsetup:: |
| | | |
| | | class Foo(object): |
| | | @reify |
| | | def jammy(self): |
| | | print('jammy called') |
| | | return 1 |
| | | from pyramid.decorator import reify |
| | | |
| | | class Foo(object): |
| | | @reify |
| | | def jammy(self): |
| | | print('jammy called') |
| | | return 1 |
| | | |
| | | And usage of Foo: |
| | | |
| | | >>> f = Foo() |
| | | >>> v = f.jammy |
| | | 'jammy called' |
| | | >>> print(v) |
| | | 1 |
| | | >>> f.jammy |
| | | 1 |
| | | >>> # jammy func not called the second time; it replaced itself with 1 |
| | | .. doctest:: |
| | | |
| | | >>> f = Foo() |
| | | >>> v = f.jammy |
| | | jammy called |
| | | >>> print(v) |
| | | 1 |
| | | >>> f.jammy |
| | | 1 |
| | | >>> # jammy func not called the second time; it replaced itself with 1 |
| | | >>> # Note: reassignment is possible |
| | | >>> f.jammy = 2 |
| | | >>> f.jammy |
| | | 2 |
| | | """ |
| | | def __init__(self, wrapped): |
| | | self.wrapped = wrapped |
| | | functools.update_wrapper(self, wrapped) |
| | | update_wrapper(self, wrapped) |
| | | |
| | | def __get__(self, inst, objtype=None): |
| | | if inst is None: |
| | |
| | | for all view configurations which do not explicitly declare their |
| | | own.""" |
| | | |
| | | class IDefaultCSRFOptions(Interface): |
| | | """ An object representing the default CSRF settings to be used for |
| | | all view configurations which do not explicitly declare their own.""" |
| | | require_csrf = Attribute( |
| | | 'Boolean attribute. If ``True``, then CSRF checks will be enabled by ' |
| | | 'default for the view unless overridden.') |
| | | token = Attribute('The key to be matched in the body of the request.') |
| | | header = Attribute('The header to be matched with the CSRF token.') |
| | | safe_methods = Attribute('A set of safe methods that skip CSRF checks.') |
| | | |
| | | class ISessionFactory(Interface): |
| | | """ An interface representing a factory which accepts a request object and |
| | | returns an ISession object """ |
| | |
| | | class IViewDeriverInfo(Interface): |
| | | """ An object implementing this interface is passed to every |
| | | :term:`view deriver` during configuration.""" |
| | | registry = Attribute('The "current" application registry when the ' |
| | | registry = Attribute('The "current" application registry where the ' |
| | | 'view was created') |
| | | package = Attribute('The "current package" when the view ' |
| | | package = Attribute('The "current package" where the view ' |
| | | 'configuration statement was found') |
| | | settings = Attribute('The deployment settings dictionary related ' |
| | | 'to the current application') |
| | |
| | | def __init__(self, func): |
| | | self.func = func |
| | | |
| | | def resolve(self): |
| | | @reify |
| | | def value(self): |
| | | return self.func() |
| | | |
| | | def resolve(self): |
| | | return self.value |
| | | |
| | | def undefer(v): |
| | | """ Function which accepts an object and returns it unless it is a |
| | | :class:`pyramid.registry.Deferred` instance. If it is an instance of |
| | |
| | | JSONP_VALID_CALLBACK = re.compile(r"^[$a-z_][$0-9a-z_\.\[\]]+[^.]$", re.I) |
| | | |
| | | class JSONP(JSON): |
| | | """ `JSONP <http://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper |
| | | """ `JSONP <https://en.wikipedia.org/wiki/JSONP>`_ renderer factory helper |
| | | which implements a hybrid json/jsonp renderer. JSONP is useful for |
| | | making cross-domain AJAX requests. |
| | | |
New file |
| | |
| | | [run] |
| | | source = {{package}} |
| | | omit = {{package}}/test* |
| | |
| | | pyramid.default_locale_name = en |
| | | pyramid.includes = |
| | | pyramid_debugtoolbar |
| | | pyramid_tm |
| | | |
| | | sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite |
| | | |
New file |
| | |
| | | [pytest] |
| | | testpaths = {{package}} |
| | | python_files = *.py |
New file |
| | |
| | | [run] |
| | | source = {{package}} |
| | | omit = {{package}}/test* |
New file |
| | |
| | | [pytest] |
| | | testpaths = {{package}} |
| | | python_files = *.py |
| | |
| | | self.make_venv(self.directory) |
| | | here = os.path.abspath(os.path.dirname(__file__)) |
| | | os.chdir(os.path.dirname(os.path.dirname(here))) |
| | | subprocess.check_call( |
| | | [os.path.join(self.directory, 'bin', 'python'), |
| | | 'setup.py', 'develop']) |
| | | pip = os.path.join(self.directory, 'bin', 'pip') |
| | | subprocess.check_call([pip, 'install', '-e', '.']) |
| | | os.chdir(self.directory) |
| | | subprocess.check_call(['bin/pcreate', '-s', tmpl_name, 'Dingle']) |
| | | os.chdir('Dingle') |
| | | py = os.path.join(self.directory, 'bin', 'python') |
| | | subprocess.check_call([py, 'setup.py', 'install']) |
| | | subprocess.check_call([pip, 'install', '.[testing]']) |
| | | if tmpl_name == 'alchemy': |
| | | populate = os.path.join(self.directory, 'bin', |
| | | 'initialize_Dingle_db') |
| | | subprocess.check_call([populate, 'development.ini']) |
| | | subprocess.check_call([py, 'setup.py', 'test']) |
| | | subprocess.check_call([ |
| | | os.path.join(self.directory, 'bin', 'py.test')]) |
| | | pserve = os.path.join(self.directory, 'bin', 'pserve') |
| | | for ininame, hastoolbar in (('development.ini', True), |
| | | ('production.ini', False)): |
New file |
| | |
| | | [run] |
| | | source = {{package}} |
| | | omit = {{package}}/test* |
New file |
| | |
| | | [pytest] |
| | | testpaths = {{package}} |
| | | python_files = *.py |
| | |
| | | |
| | | return pickle.loads(pickled) |
| | | |
| | | |
| | | def check_csrf_origin(request, trusted_origins=None, raises=True): |
| | | """ |
| | | Check the Origin of the request to see if it is a cross site request or |
| | |
| | | "pyramid.csrf_trusted_origins", []) |
| | | ) |
| | | |
| | | if request.host_port not in set([80, 443]): |
| | | if request.host_port not in set(["80", "443"]): |
| | | trusted_origins.append("{0.domain}:{0.host_port}".format(request)) |
| | | else: |
| | | trusted_origins.append(request.domain) |
| | |
| | | considered valid. It must be passed in either the request body or |
| | | a header. |
| | | """ |
| | | supplied_token = "" |
| | | # If this is a POST/PUT/etc request, then we'll check the body to see if it |
| | | # has a token. We explicitly use request.POST here because CSRF tokens |
| | | # should never appear in an URL as doing so is a security issue. We also |
| | | # explicitly check for request.POST here as we do not support sending form |
| | | # encoded data over anything but a request.POST. |
| | | supplied_token = request.POST.get(token, "") |
| | | if token is not None: |
| | | supplied_token = request.POST.get(token, "") |
| | | |
| | | # If we were unable to locate a CSRF token in a request body, then we'll |
| | | # check to see if there are any headers that have a value for us. |
| | | if supplied_token == "": |
| | | if supplied_token == "" and header is not None: |
| | | supplied_token = request.headers.get(header, "") |
| | | |
| | | expected_token = request.session.get_csrf_token() |
| | |
| | | intr = D['introspectable'] |
| | | self.assertEqual(intr['value'], 'perm') |
| | | |
| | | def test_set_default_csrf_options(self): |
| | | from pyramid.interfaces import IDefaultCSRFOptions |
| | | config = self._makeOne(autocommit=True) |
| | | config.set_default_csrf_options() |
| | | result = config.registry.getUtility(IDefaultCSRFOptions) |
| | | self.assertEqual(result.require_csrf, True) |
| | | self.assertEqual(result.token, 'csrf_token') |
| | | self.assertEqual(result.header, 'X-CSRF-Token') |
| | | self.assertEqual(list(sorted(result.safe_methods)), |
| | | ['GET', 'HEAD', 'OPTIONS', 'TRACE']) |
| | | |
| | | def test_changing_set_default_csrf_options(self): |
| | | from pyramid.interfaces import IDefaultCSRFOptions |
| | | config = self._makeOne(autocommit=True) |
| | | config.set_default_csrf_options( |
| | | require_csrf=False, token='DUMMY', header=None, safe_methods=('PUT',)) |
| | | result = config.registry.getUtility(IDefaultCSRFOptions) |
| | | self.assertEqual(result.require_csrf, False) |
| | | self.assertEqual(result.token, 'DUMMY') |
| | | self.assertEqual(result.header, None) |
| | | self.assertEqual(list(sorted(result.safe_methods)), ['PUT']) |
| | |
| | | inst = self._makeOne() |
| | | cb = DummyCacheBuster('foo') |
| | | inst.add_cache_buster(config, here, cb) |
| | | self.assertEqual(inst.cache_busters, [(here + '/', cb, False)]) |
| | | self.assertEqual(inst.cache_busters, [(here + os.sep, cb, False)]) |
| | | |
| | | def test_add_cachebuster_overwrite(self): |
| | | config = DummyConfig() |
| | |
| | | import os |
| | | import unittest |
| | | from pyramid.tests.test_scripts import dummy |
| | | |
| | |
| | | from pyramid.renderers import null_renderer as nr |
| | | config = self._makeConfig(autocommit=True) |
| | | config.add_static_view('static', 'static', cache_max_age=3600) |
| | | config.add_static_view(name='static2', path='/var/www/static') |
| | | path2 = os.path.normpath('/var/www/static') |
| | | config.add_static_view(name='static2', path=path2) |
| | | config.add_static_view( |
| | | name='pyramid_scaffold', |
| | | path='pyramid:scaffolds/starter/+package+/static' |
| | |
| | | expected = [ |
| | | ['__static/', '/static/*subpath', |
| | | 'pyramid.tests.test_scripts:static/', '*'], |
| | | ['__static2/', '/static2/*subpath', '/var/www/static/', '*'], |
| | | ['__static2/', '/static2/*subpath', path2 + os.sep, '*'], |
| | | ['__pyramid_scaffold/', '/pyramid_scaffold/*subpath', |
| | | 'pyramid:scaffolds/starter/+package+/static/', '*'], |
| | | ] |
| | |
| | | request = testing.DummyRequest() |
| | | request.scheme = "https" |
| | | request.host = "example.com" |
| | | request.host_port = 443 |
| | | request.host_port = "443" |
| | | request.referrer = "https://example.com/login/" |
| | | request.registry.settings = {} |
| | | self.assertTrue(self._callFUT(request)) |
| | |
| | | request = testing.DummyRequest() |
| | | request.scheme = "https" |
| | | request.host = "example.com" |
| | | request.host_port = 443 |
| | | request.host_port = "443" |
| | | request.headers = {"Origin": "https://example.com/"} |
| | | request.referrer = "https://not-example.com/" |
| | | request.registry.settings = {} |
| | |
| | | request = testing.DummyRequest() |
| | | request.scheme = "https" |
| | | request.host = "example.com" |
| | | request.host_port = 443 |
| | | request.host_port = "443" |
| | | request.referrer = "https://not-example.com/login/" |
| | | request.registry.settings = { |
| | | "pyramid.csrf_trusted_origins": ["not-example.com"], |
| | |
| | | request = testing.DummyRequest() |
| | | request.scheme = "https" |
| | | request.host = "example.com:8080" |
| | | request.host_port = 8080 |
| | | request.host_port = "8080" |
| | | request.referrer = "https://example.com:8080/login/" |
| | | request.registry.settings = {} |
| | | self.assertTrue(self._callFUT(request)) |
| | |
| | | request = testing.DummyRequest() |
| | | request.scheme = "https" |
| | | request.host = "example.com" |
| | | request.host_port = 443 |
| | | request.host_port = "443" |
| | | request.referrer = "https://not-example.com/login/" |
| | | request.registry.settings = {} |
| | | self.assertRaises(BadCSRFOrigin, self._callFUT, request) |
| | |
| | | request = testing.DummyRequest() |
| | | request.scheme = "https" |
| | | request.host = "example.com" |
| | | request.host_port = 443 |
| | | request.host_port = "443" |
| | | request.referrer = "http://example.com/evil/" |
| | | request.registry.settings = {} |
| | | self.assertRaises(BadCSRFOrigin, self._callFUT, request) |
| | |
| | | request = testing.DummyRequest() |
| | | request.scheme = "https" |
| | | request.host = "example.com:8080" |
| | | request.host_port = 8080 |
| | | request.host_port = "8080" |
| | | request.referrer = "https://example.com/login/" |
| | | request.registry.settings = {} |
| | | self.assertRaises(BadCSRFOrigin, self._callFUT, request) |
New file |
| | |
| | | import unittest |
| | | from pyramid import testing |
| | | |
| | | class Test_excview_tween_factory(unittest.TestCase): |
| | | def setUp(self): |
| | | self.config = testing.setUp() |
| | | |
| | | def tearDown(self): |
| | | testing.tearDown() |
| | | |
| | | def _makeOne(self, handler, registry=None): |
| | | from pyramid.tweens import excview_tween_factory |
| | | if registry is None: |
| | | registry = self.config.registry |
| | | return excview_tween_factory(handler, registry) |
| | | |
| | | def test_it_passthrough_no_exception(self): |
| | | dummy_response = DummyResponse() |
| | | def handler(request): |
| | | return dummy_response |
| | | tween = self._makeOne(handler) |
| | | request = DummyRequest() |
| | | result = tween(request) |
| | | self.assertTrue(result is dummy_response) |
| | | |
| | | def test_it_catches_notfound(self): |
| | | from pyramid.request import Request |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | self.config.add_notfound_view(lambda exc, request: exc) |
| | | def handler(request): |
| | | raise HTTPNotFound |
| | | tween = self._makeOne(handler) |
| | | request = Request.blank('/') |
| | | result = tween(request) |
| | | self.assertEqual(result.status, '404 Not Found') |
| | | |
| | | def test_it_catches_with_predicate(self): |
| | | from pyramid.request import Request |
| | | from pyramid.response import Response |
| | | def excview(request): |
| | | return Response('foo') |
| | | self.config.add_view(excview, context=ValueError, request_method='GET') |
| | | def handler(request): |
| | | raise ValueError |
| | | tween = self._makeOne(handler) |
| | | request = Request.blank('/') |
| | | result = tween(request) |
| | | self.assertTrue(b'foo' in result.body) |
| | | |
| | | def test_it_reraises_on_mismatch(self): |
| | | from pyramid.request import Request |
| | | def excview(request): pass |
| | | self.config.add_view(excview, context=ValueError, request_method='GET') |
| | | def handler(request): |
| | | raise ValueError |
| | | tween = self._makeOne(handler) |
| | | request = Request.blank('/') |
| | | request.method = 'POST' |
| | | self.assertRaises(ValueError, lambda: tween(request)) |
| | | |
| | | def test_it_reraises_on_no_match(self): |
| | | from pyramid.request import Request |
| | | def handler(request): |
| | | raise ValueError |
| | | tween = self._makeOne(handler) |
| | | request = Request.blank('/') |
| | | self.assertRaises(ValueError, lambda: tween(request)) |
| | | |
| | | class DummyRequest: |
| | | pass |
| | | |
| | | class DummyResponse: |
| | | pass |
| | |
| | | else: # pragma: no cover |
| | | self.fail() |
| | | |
| | | def test_it_raises_if_not_found(self): |
| | | from pyramid.httpexceptions import HTTPNotFound |
| | | request = self._makeOne() |
| | | dummy_exc = RuntimeError() |
| | | try: |
| | | raise dummy_exc |
| | | except RuntimeError: |
| | | self.assertRaises(HTTPNotFound, request.invoke_exception_view) |
| | | else: # pragma: no cover |
| | | self.fail() |
| | | |
| | | def test_it_raises_predicate_mismatch(self): |
| | | from pyramid.exceptions import PredicateMismatch |
| | | def exc_view(exc, request): pass |
| | | self.config.add_view(exc_view, context=Exception, request_method='POST') |
| | | request = self._makeOne() |
| | | request.method = 'GET' |
| | | dummy_exc = RuntimeError() |
| | | try: |
| | | raise dummy_exc |
| | | except RuntimeError: |
| | | self.assertRaises(PredicateMismatch, request.invoke_exception_view) |
| | | else: # pragma: no cover |
| | | self.fail() |
| | | |
| | | class ExceptionResponse(Exception): |
| | | status = '404 Not Found' |
| | | app_iter = ['Not Found'] |
| | |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_secured_view_skipped_by_default_on_exception_view(self): |
| | | from pyramid.request import Request |
| | | from pyramid.security import NO_PERMISSION_REQUIRED |
| | | def view(request): |
| | | raise ValueError |
| | | def excview(request): |
| | | return 'hello' |
| | | self._registerSecurityPolicy(False) |
| | | self.config.add_settings({'debug_authorization': True}) |
| | | self.config.set_default_permission('view') |
| | | self.config.add_view(view, name='foo', permission=NO_PERMISSION_REQUIRED) |
| | | self.config.add_view(excview, context=ValueError, renderer='string') |
| | | app = self.config.make_wsgi_app() |
| | | request = Request.blank('/foo', base_url='http://example.com') |
| | | request.method = 'POST' |
| | | response = request.get_response(app) |
| | | self.assertTrue(b'hello' in response.body) |
| | | |
| | | def test_secured_view_failed_on_explicit_exception_view(self): |
| | | from pyramid.httpexceptions import HTTPForbidden |
| | | from pyramid.request import Request |
| | | from pyramid.security import NO_PERMISSION_REQUIRED |
| | | def view(request): |
| | | raise ValueError |
| | | def excview(request): pass |
| | | self._registerSecurityPolicy(False) |
| | | self.config.add_view(view, name='foo', permission=NO_PERMISSION_REQUIRED) |
| | | self.config.add_view(excview, context=ValueError, renderer='string', |
| | | permission='view') |
| | | app = self.config.make_wsgi_app() |
| | | request = Request.blank('/foo', base_url='http://example.com') |
| | | request.method = 'POST' |
| | | try: |
| | | request.get_response(app) |
| | | except HTTPForbidden: |
| | | pass |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_secured_view_passed_on_explicit_exception_view(self): |
| | | from pyramid.request import Request |
| | | from pyramid.security import NO_PERMISSION_REQUIRED |
| | | def view(request): |
| | | raise ValueError |
| | | def excview(request): |
| | | return 'hello' |
| | | self._registerSecurityPolicy(True) |
| | | self.config.add_view(view, name='foo', permission=NO_PERMISSION_REQUIRED) |
| | | self.config.add_view(excview, context=ValueError, renderer='string', |
| | | permission='view') |
| | | app = self.config.make_wsgi_app() |
| | | request = Request.blank('/foo', base_url='http://example.com') |
| | | request.method = 'POST' |
| | | request.headers['X-CSRF-Token'] = 'foo' |
| | | response = request.get_response(app) |
| | | self.assertTrue(b'hello' in response.body) |
| | | |
| | | def test_predicate_mismatch_view_has_no_name(self): |
| | | from pyramid.exceptions import PredicateMismatch |
| | | response = DummyResponse() |
| | |
| | | self.assertRaises(ConfigurationError, self.config._derive_view, |
| | | view, http_cache=(None,)) |
| | | |
| | | def test_csrf_view_requires_bool_or_str_in_require_csrf(self): |
| | | def view(request): pass |
| | | try: |
| | | self.config._derive_view(view, require_csrf=object()) |
| | | except ConfigurationError as ex: |
| | | self.assertEqual( |
| | | 'View option "require_csrf" must be a string or boolean value', |
| | | ex.args[0]) |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_csrf_view_requires_bool_or_str_in_config_setting(self): |
| | | def view(request): pass |
| | | self.config.add_settings({'pyramid.require_default_csrf': object()}) |
| | | try: |
| | | self.config._derive_view(view) |
| | | except ConfigurationError as ex: |
| | | self.assertEqual( |
| | | 'Config setting "pyramid.require_csrf_default" must be a ' |
| | | 'string or boolean value', |
| | | ex.args[0]) |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_csrf_view_requires_header(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | | return response |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | request.method = 'POST' |
| | | request.POST = {} |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.headers = {'X-CSRF-Token': 'foo'} |
| | | view = self.config._derive_view(inner_view, require_csrf=True) |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | |
| | | def test_csrf_view_requires_param(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | | return response |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.POST = {'DUMMY': 'foo'} |
| | | view = self.config._derive_view(inner_view, require_csrf='DUMMY') |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | |
| | | def test_csrf_view_https_domain(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | | return response |
| | | request = self._makeRequest() |
| | | request.scheme = "https" |
| | | request.domain = "example.com" |
| | | request.host_port = 443 |
| | | request.referrer = "https://example.com/login/" |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.POST = {'DUMMY': 'foo'} |
| | | view = self.config._derive_view(inner_view, require_csrf='DUMMY') |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | |
| | | def test_csrf_view_ignores_GET(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | |
| | | def test_csrf_view_fails_on_bad_POST_param(self): |
| | | def test_csrf_view_fails_with_bad_POST_header(self): |
| | | from pyramid.exceptions import BadCSRFToken |
| | | def inner_view(request): pass |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.POST = {'DUMMY': 'bar'} |
| | | view = self.config._derive_view(inner_view, require_csrf='DUMMY') |
| | | request.headers = {'X-CSRF-Token': 'bar'} |
| | | view = self.config._derive_view(inner_view, require_csrf=True) |
| | | self.assertRaises(BadCSRFToken, lambda: view(None, request)) |
| | | |
| | | def test_csrf_view_fails_on_bad_POST_header(self): |
| | | def test_csrf_view_passes_with_good_POST_header(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | | return response |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.headers = {'X-CSRF-Token': 'foo'} |
| | | view = self.config._derive_view(inner_view, require_csrf=True) |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | |
| | | def test_csrf_view_fails_with_bad_POST_token(self): |
| | | from pyramid.exceptions import BadCSRFToken |
| | | def inner_view(request): pass |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | request.method = 'POST' |
| | | request.POST = {} |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.headers = {'X-CSRF-Token': 'bar'} |
| | | view = self.config._derive_view(inner_view, require_csrf='DUMMY') |
| | | request.POST = {'csrf_token': 'bar'} |
| | | view = self.config._derive_view(inner_view, require_csrf=True) |
| | | self.assertRaises(BadCSRFToken, lambda: view(None, request)) |
| | | |
| | | def test_csrf_view_fails_on_bad_PUT_header(self): |
| | | from pyramid.exceptions import BadCSRFToken |
| | | def inner_view(request): pass |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | request.method = 'PUT' |
| | | request.POST = {} |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.headers = {'X-CSRF-Token': 'bar'} |
| | | view = self.config._derive_view(inner_view, require_csrf='DUMMY') |
| | | self.assertRaises(BadCSRFToken, lambda: view(None, request)) |
| | | |
| | | def test_csrf_view_fails_on_bad_referrer(self): |
| | | from pyramid.exceptions import BadCSRFOrigin |
| | | def inner_view(request): pass |
| | | request = self._makeRequest() |
| | | request.method = "POST" |
| | | request.scheme = "https" |
| | | request.host_port = 443 |
| | | request.domain = "example.com" |
| | | request.referrer = "https://not-example.com/evil/" |
| | | request.registry.settings = {} |
| | | view = self.config._derive_view(inner_view, require_csrf='DUMMY') |
| | | self.assertRaises(BadCSRFOrigin, lambda: view(None, request)) |
| | | |
| | | def test_csrf_view_fails_on_bad_origin(self): |
| | | from pyramid.exceptions import BadCSRFOrigin |
| | | def inner_view(request): pass |
| | | request = self._makeRequest() |
| | | request.method = "POST" |
| | | request.scheme = "https" |
| | | request.host_port = 443 |
| | | request.domain = "example.com" |
| | | request.headers = {"Origin": "https://not-example.com/evil/"} |
| | | request.registry.settings = {} |
| | | view = self.config._derive_view(inner_view, require_csrf='DUMMY') |
| | | self.assertRaises(BadCSRFOrigin, lambda: view(None, request)) |
| | | |
| | | def test_csrf_view_uses_config_setting_truthy(self): |
| | | def test_csrf_view_passes_with_good_POST_token(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | | return response |
| | |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.POST = {'csrf_token': 'foo'} |
| | | self.config.add_settings({'pyramid.require_default_csrf': 'yes'}) |
| | | view = self.config._derive_view(inner_view) |
| | | view = self.config._derive_view(inner_view, require_csrf=True) |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | |
| | | def test_csrf_view_uses_config_setting_with_custom_token(self): |
| | | def test_csrf_view_https_domain(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | | return response |
| | | request = self._makeRequest() |
| | | request.scheme = "https" |
| | | request.domain = "example.com" |
| | | request.host_port = "443" |
| | | request.referrer = "https://example.com/login/" |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.POST = {'csrf_token': 'foo'} |
| | | view = self.config._derive_view(inner_view, require_csrf=True) |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | |
| | | def test_csrf_view_fails_on_bad_PUT_header(self): |
| | | from pyramid.exceptions import BadCSRFToken |
| | | def inner_view(request): pass |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | request.method = 'PUT' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.headers = {'X-CSRF-Token': 'bar'} |
| | | view = self.config._derive_view(inner_view, require_csrf=True) |
| | | self.assertRaises(BadCSRFToken, lambda: view(None, request)) |
| | | |
| | | def test_csrf_view_fails_on_bad_referrer(self): |
| | | from pyramid.exceptions import BadCSRFOrigin |
| | | def inner_view(request): pass |
| | | request = self._makeRequest() |
| | | request.method = "POST" |
| | | request.scheme = "https" |
| | | request.host_port = "443" |
| | | request.domain = "example.com" |
| | | request.referrer = "https://not-example.com/evil/" |
| | | request.registry.settings = {} |
| | | view = self.config._derive_view(inner_view, require_csrf=True) |
| | | self.assertRaises(BadCSRFOrigin, lambda: view(None, request)) |
| | | |
| | | def test_csrf_view_fails_on_bad_origin(self): |
| | | from pyramid.exceptions import BadCSRFOrigin |
| | | def inner_view(request): pass |
| | | request = self._makeRequest() |
| | | request.method = "POST" |
| | | request.scheme = "https" |
| | | request.host_port = "443" |
| | | request.domain = "example.com" |
| | | request.headers = {"Origin": "https://not-example.com/evil/"} |
| | | request.registry.settings = {} |
| | | view = self.config._derive_view(inner_view, require_csrf=True) |
| | | self.assertRaises(BadCSRFOrigin, lambda: view(None, request)) |
| | | |
| | | def test_csrf_view_enabled_by_default(self): |
| | | from pyramid.exceptions import BadCSRFToken |
| | | def inner_view(request): pass |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | self.config.set_default_csrf_options(require_csrf=True) |
| | | view = self.config._derive_view(inner_view) |
| | | self.assertRaises(BadCSRFToken, lambda: view(None, request)) |
| | | |
| | | def test_csrf_view_uses_custom_csrf_token(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | | return response |
| | |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.POST = {'DUMMY': 'foo'} |
| | | self.config.add_settings({'pyramid.require_default_csrf': 'DUMMY'}) |
| | | self.config.set_default_csrf_options(require_csrf=True, token='DUMMY') |
| | | view = self.config._derive_view(inner_view) |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | |
| | | def test_csrf_view_uses_config_setting_falsey(self): |
| | | def test_csrf_view_uses_custom_csrf_header(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | | return response |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.params['csrf_token'] = 'foo' |
| | | self.config.add_settings({'pyramid.require_default_csrf': 'no'}) |
| | | request.headers = {'DUMMY': 'foo'} |
| | | self.config.set_default_csrf_options(require_csrf=True, header='DUMMY') |
| | | view = self.config._derive_view(inner_view) |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | |
| | | def test_csrf_view_uses_custom_methods(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | | return response |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | request.method = 'PUT' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | self.config.set_default_csrf_options( |
| | | require_csrf=True, safe_methods=['PUT']) |
| | | view = self.config._derive_view(inner_view) |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | |
| | | request.scheme = "http" |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.POST = {'DUMMY': 'foo'} |
| | | self.config.add_settings({'pyramid.require_default_csrf': 'yes'}) |
| | | view = self.config._derive_view(inner_view, require_csrf='DUMMY') |
| | | request.POST = {'csrf_token': 'bar'} |
| | | self.config.set_default_csrf_options(require_csrf=True) |
| | | view = self.config._derive_view(inner_view, require_csrf=False) |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | |
| | | def test_csrf_view_uses_config_setting_when_view_option_is_true(self): |
| | | response = DummyResponse() |
| | | def inner_view(request): |
| | | return response |
| | | request = self._makeRequest() |
| | | request.scheme = "http" |
| | | def test_csrf_view_skipped_by_default_on_exception_view(self): |
| | | from pyramid.request import Request |
| | | def view(request): |
| | | raise ValueError |
| | | def excview(request): |
| | | return 'hello' |
| | | self.config.set_default_csrf_options(require_csrf=True) |
| | | self.config.set_session_factory( |
| | | lambda request: DummySession({'csrf_token': 'foo'})) |
| | | self.config.add_view(view, name='foo', require_csrf=False) |
| | | self.config.add_view(excview, context=ValueError, renderer='string') |
| | | app = self.config.make_wsgi_app() |
| | | request = Request.blank('/foo', base_url='http://example.com') |
| | | request.method = 'POST' |
| | | request.session = DummySession({'csrf_token': 'foo'}) |
| | | request.POST = {'DUMMY': 'foo'} |
| | | self.config.add_settings({'pyramid.require_default_csrf': 'DUMMY'}) |
| | | view = self.config._derive_view(inner_view, require_csrf=True) |
| | | result = view(None, request) |
| | | self.assertTrue(result is response) |
| | | response = request.get_response(app) |
| | | self.assertTrue(b'hello' in response.body) |
| | | |
| | | def test_csrf_view_failed_on_explicit_exception_view(self): |
| | | from pyramid.exceptions import BadCSRFToken |
| | | from pyramid.request import Request |
| | | def view(request): |
| | | raise ValueError |
| | | def excview(request): pass |
| | | self.config.set_default_csrf_options(require_csrf=True) |
| | | self.config.set_session_factory( |
| | | lambda request: DummySession({'csrf_token': 'foo'})) |
| | | self.config.add_view(view, name='foo', require_csrf=False) |
| | | self.config.add_view(excview, context=ValueError, renderer='string', |
| | | require_csrf=True) |
| | | app = self.config.make_wsgi_app() |
| | | request = Request.blank('/foo', base_url='http://example.com') |
| | | request.method = 'POST' |
| | | try: |
| | | request.get_response(app) |
| | | except BadCSRFToken: |
| | | pass |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_csrf_view_passed_on_explicit_exception_view(self): |
| | | from pyramid.request import Request |
| | | def view(request): |
| | | raise ValueError |
| | | def excview(request): |
| | | return 'hello' |
| | | self.config.set_default_csrf_options(require_csrf=True) |
| | | self.config.set_session_factory( |
| | | lambda request: DummySession({'csrf_token': 'foo'})) |
| | | self.config.add_view(view, name='foo', require_csrf=False) |
| | | self.config.add_view(excview, context=ValueError, renderer='string', |
| | | require_csrf=True) |
| | | app = self.config.make_wsgi_app() |
| | | request = Request.blank('/foo', base_url='http://example.com') |
| | | request.method = 'POST' |
| | | request.headers['X-CSRF-Token'] = 'foo' |
| | | response = request.get_response(app) |
| | | self.assertTrue(b'hello' in response.body) |
| | | |
| | | |
| | | class TestDerivationOrder(unittest.TestCase): |
| | |
| | | from pyramid.interfaces import IRequest |
| | | from pyramid.interfaces import IView |
| | | from pyramid.interfaces import IViewClassifier |
| | | from pyramid.interfaces import IExceptionViewClassifier |
| | | classifier = IViewClassifier |
| | | if ctx_iface is None: |
| | | ctx_iface = Interface |
| | |
| | | environ = {} |
| | | self.environ = environ |
| | | self.params = {} |
| | | self.POST = {} |
| | | self.cookies = {} |
| | | self.headers = {} |
| | | self.response = DummyResponse() |
| | |
| | | import sys |
| | | |
| | | from pyramid.compat import reraise |
| | | from pyramid.exceptions import PredicateMismatch |
| | | from pyramid.interfaces import ( |
| | | IExceptionViewClassifier, |
| | | IRequest, |
| | |
| | | # https://github.com/Pylons/pyramid/issues/700 |
| | | request_iface = attrs.get('request_iface', IRequest) |
| | | provides = providedBy(exc) |
| | | response = _call_view( |
| | | registry, |
| | | request, |
| | | exc, |
| | | provides, |
| | | '', |
| | | view_classifier=IExceptionViewClassifier, |
| | | request_iface=request_iface.combined |
| | | ) |
| | | try: |
| | | response = _call_view( |
| | | registry, |
| | | request, |
| | | exc, |
| | | provides, |
| | | '', |
| | | view_classifier=IExceptionViewClassifier, |
| | | request_iface=request_iface.combined |
| | | ) |
| | | |
| | | # if views matched but did not pass predicates, squash the error |
| | | # and re-raise the original exception |
| | | except PredicateMismatch: |
| | | response = None |
| | | |
| | | # re-raise the original exception as no exception views were |
| | | # able to handle the error |
| | | if response is None: |
| | | raise |
| | | reraise(*attrs['exc_info']) |
| | | |
| | | return response |
| | | |
| | |
| | | |
| | | from pyramid.httpexceptions import ( |
| | | HTTPFound, |
| | | HTTPNotFound, |
| | | default_exceptionresponse_view, |
| | | ) |
| | | |
| | |
| | | object that this method is attached to as the ``request``, and |
| | | ``True`` for ``secure``. |
| | | |
| | | This method returns a :term:`response` object or ``None`` if no |
| | | matching exception view can be found.""" |
| | | This method returns a :term:`response` object or raises |
| | | :class:`pyramid.httpexceptions.HTTPNotFound` if a matching view cannot |
| | | be found.""" |
| | | |
| | | if request is None: |
| | | request = self |
| | |
| | | secure=secure, |
| | | request_iface=request_iface.combined, |
| | | ) |
| | | if response is None: |
| | | raise HTTPNotFound |
| | | return response |
| | |
| | | from pyramid.interfaces import ( |
| | | IAuthenticationPolicy, |
| | | IAuthorizationPolicy, |
| | | IDefaultCSRFOptions, |
| | | IDefaultPermission, |
| | | IDebugLogger, |
| | | IResponse, |
| | | IViewMapper, |
| | |
| | | ) |
| | | |
| | | from pyramid.compat import ( |
| | | string_types, |
| | | is_bound_method, |
| | | is_unbound_method, |
| | | ) |
| | |
| | | PredicateMismatch, |
| | | ) |
| | | from pyramid.httpexceptions import HTTPForbidden |
| | | from pyramid.settings import ( |
| | | falsey, |
| | | truthy, |
| | | ) |
| | | from pyramid.util import object_description |
| | | from pyramid.view import render_view_to_response |
| | | from pyramid import renderers |
| | |
| | | secured_view.options = ('permission',) |
| | | |
| | | def _secured_view(view, info): |
| | | permission = info.options.get('permission') |
| | | permission = explicit_val = info.options.get('permission') |
| | | if permission is None: |
| | | permission = info.registry.queryUtility(IDefaultPermission) |
| | | if permission == NO_PERMISSION_REQUIRED: |
| | | # allow views registered within configurations that have a |
| | | # default permission to explicitly override the default |
| | |
| | | principals = authn_policy.effective_principals(request) |
| | | return authz_policy.permits(context, principals, permission) |
| | | def _secured_view(context, request): |
| | | if ( |
| | | getattr(request, 'exception', None) is not None and |
| | | explicit_val is None |
| | | ): |
| | | return view(context, request) |
| | | |
| | | result = _permitted(context, request) |
| | | if result: |
| | | return view(context, request) |
| | |
| | | def _authdebug_view(view, info): |
| | | wrapped_view = view |
| | | settings = info.settings |
| | | permission = info.options.get('permission') |
| | | permission = explicit_val = info.options.get('permission') |
| | | if permission is None: |
| | | permission = info.registry.queryUtility(IDefaultPermission) |
| | | authn_policy = info.registry.queryUtility(IAuthenticationPolicy) |
| | | authz_policy = info.registry.queryUtility(IAuthorizationPolicy) |
| | | logger = info.registry.queryUtility(IDebugLogger) |
| | | if settings and settings.get('debug_authorization', False): |
| | | def _authdebug_view(context, request): |
| | | if ( |
| | | getattr(request, 'exception', None) is not None and |
| | | explicit_val is None |
| | | ): |
| | | return view(context, request) |
| | | |
| | | view_name = getattr(request, 'view_name', None) |
| | | |
| | | if authn_policy and authz_policy: |
| | |
| | | |
| | | decorated_view.options = ('decorator',) |
| | | |
| | | def _parse_csrf_setting(val, error_source): |
| | | if val: |
| | | if isinstance(val, string_types): |
| | | if val.lower() in truthy: |
| | | val = True |
| | | elif val.lower() in falsey: |
| | | val = False |
| | | elif not isinstance(val, bool): |
| | | raise ConfigurationError( |
| | | '{0} must be a string or boolean value' |
| | | .format(error_source)) |
| | | return val |
| | | |
| | | SAFE_REQUEST_METHODS = frozenset(["GET", "HEAD", "OPTIONS", "TRACE"]) |
| | | |
| | | def csrf_view(view, info): |
| | | default_val = _parse_csrf_setting( |
| | | info.settings.get('pyramid.require_default_csrf'), |
| | | 'Config setting "pyramid.require_csrf_default"') |
| | | val = _parse_csrf_setting( |
| | | info.options.get('require_csrf'), |
| | | 'View option "require_csrf"') |
| | | if (val is True and default_val) or val is None: |
| | | val = default_val |
| | | if val is True: |
| | | val = 'csrf_token' |
| | | explicit_val = info.options.get('require_csrf') |
| | | defaults = info.registry.queryUtility(IDefaultCSRFOptions) |
| | | if defaults is None: |
| | | default_val = False |
| | | token = 'csrf_token' |
| | | header = 'X-CSRF-Token' |
| | | safe_methods = frozenset(["GET", "HEAD", "OPTIONS", "TRACE"]) |
| | | else: |
| | | default_val = defaults.require_csrf |
| | | token = defaults.token |
| | | header = defaults.header |
| | | safe_methods = defaults.safe_methods |
| | | enabled = ( |
| | | explicit_val is True or |
| | | (explicit_val is not False and default_val) |
| | | ) |
| | | # disable if both header and token are disabled |
| | | enabled = enabled and (token or header) |
| | | wrapped_view = view |
| | | if val: |
| | | if enabled: |
| | | def csrf_view(context, request): |
| | | # Assume that anything not defined as 'safe' by RFC2616 needs |
| | | # protection |
| | | if request.method not in SAFE_REQUEST_METHODS: |
| | | if ( |
| | | request.method not in safe_methods and |
| | | ( |
| | | # skip exception views unless value is explicitly defined |
| | | getattr(request, 'exception', None) is None or |
| | | explicit_val is not None |
| | | ) |
| | | ): |
| | | check_csrf_origin(request, raises=True) |
| | | check_csrf_token(request, val, raises=True) |
| | | check_csrf_token(request, token, header, raises=True) |
| | | return view(context, request) |
| | | wrapped_view = csrf_view |
| | | return wrapped_view |
| | |
| | | #!/bin/bash |
| | | tox -e{py26,py27,py32,py33,py34,pypy,pypy3}-scaffolds, |
| | | tox -e{py27,py33,py34,pypy}-scaffolds, |
| | | |
| | |
| | | [tox] |
| | | envlist = |
| | | py27,py33,py34,py35,pypy,pypy3, |
| | | py27,py33,py34,py35,pypy, |
| | | docs,pep8, |
| | | {py2,py3}-cover,coverage, |
| | | |
| | |
| | | py34: python3.4 |
| | | py35: python3.5 |
| | | pypy: pypy |
| | | pypy3: pypy3 |
| | | py2: python2.7 |
| | | py3: python3.5 |
| | | |
| | |
| | | python pyramid/scaffolds/tests.py |
| | | deps = virtualenv |
| | | |
| | | [testenv:py32-scaffolds] |
| | | basepython = python3.2 |
| | | commands = |
| | | python pyramid/scaffolds/tests.py |
| | | deps = virtualenv |
| | | |
| | | [testenv:py33-scaffolds] |
| | | basepython = python3.4 |
| | | basepython = python3.3 |
| | | commands = |
| | | python pyramid/scaffolds/tests.py |
| | | deps = virtualenv |
| | |
| | | python pyramid/scaffolds/tests.py |
| | | deps = virtualenv |
| | | |
| | | [testenv:pypy-scaffolds] |
| | | basepython = pypy |
| | | [testenv:py35-scaffolds] |
| | | basepython = python3.5 |
| | | commands = |
| | | python pyramid/scaffolds/tests.py |
| | | deps = virtualenv |
| | | |
| | | [testenv:pypy3-scaffolds] |
| | | basepython = pypy3 |
| | | [testenv:pypy-scaffolds] |
| | | basepython = pypy |
| | | commands = |
| | | python pyramid/scaffolds/tests.py |
| | | deps = virtualenv |
| | |
| | | whitelist_externals = make |
| | | commands = |
| | | pip install pyramid[docs] |
| | | make -C docs html epub BUILDDIR={envdir} "SPHINXOPTS=-W -E" |
| | | make -C docs doctest html epub BUILDDIR={envdir} "SPHINXOPTS=-W -E" |
| | | |
| | | [testenv:pdf] |
| | | basepython = python3.5 |
| | | whitelist_externals = make |
| | | commands = |
| | | pip install pyramid[docs] |
| | | make -C docs latexpdf BUILDDIR={envdir} "SPHINXOPTS=-W -E" |
| | | |
| | | # we separate coverage into its own testenv because a) "last run wins" wrt |
| | | # cobertura jenkins reporting and b) pypy and jython can't handle any |