Chris McDonough
2016-06-01 3e9a737500e79a6a919ce53db9557c75d874b84c
Merge branch 'master' of github.com:Pylons/pyramid
8 files added
79 files modified
2445 ■■■■■ changed files
.travis.yml 2 ●●●●● patch | view | raw | blame | history
CHANGES.txt 126 ●●●●● patch | view | raw | blame | history
CONTRIBUTORS.txt 4 ●●●● patch | view | raw | blame | history
HISTORY.txt 241 ●●●●● patch | view | raw | blame | history
README.rst 6 ●●●●● patch | view | raw | blame | history
RELEASING.txt 84 ●●●● patch | view | raw | blame | history
appveyor.yml 12 ●●●●● patch | view | raw | blame | history
contributing.md 10 ●●●● patch | view | raw | blame | history
docs/Makefile 22 ●●●●● patch | view | raw | blame | history
docs/api/config.rst 2 ●●●●● patch | view | raw | blame | history
docs/api/index.rst 2 ●●● patch | view | raw | blame | history
docs/api/interfaces.rst 3 ●●●●● patch | view | raw | blame | history
docs/authorintro.rst 20 ●●●●● patch | view | raw | blame | history
docs/conf.py 1 ●●●● patch | view | raw | blame | history
docs/conventions.rst 8 ●●●● patch | view | raw | blame | history
docs/copyright.rst 11 ●●●● patch | view | raw | blame | history
docs/designdefense.rst 65 ●●●● patch | view | raw | blame | history
docs/glossary.rst 98 ●●●● patch | view | raw | blame | history
docs/index.rst 23 ●●●●● patch | view | raw | blame | history
docs/latexindex.rst 65 ●●●● patch | view | raw | blame | history
docs/narr/extconfig.rst 1 ●●●● patch | view | raw | blame | history
docs/narr/firstapp.rst 2 ●●● patch | view | raw | blame | history
docs/narr/hooks.rst 67 ●●●●● patch | view | raw | blame | history
docs/narr/i18n.rst 4 ●●●● patch | view | raw | blame | history
docs/narr/install.rst 4 ●●●● patch | view | raw | blame | history
docs/narr/introduction.rst 12 ●●●● patch | view | raw | blame | history
docs/narr/introspector.rst 25 ●●●●● patch | view | raw | blame | history
docs/narr/logging.rst 3 ●●●● patch | view | raw | blame | history
docs/narr/muchadoabouttraversal.rst 4 ●●● patch | view | raw | blame | history
docs/narr/project.rst 74 ●●●●● patch | view | raw | blame | history
docs/narr/renderers.rst 2 ●●● patch | view | raw | blame | history
docs/narr/sessions.rst 113 ●●●●● patch | view | raw | blame | history
docs/narr/templates.rst 2 ●●● patch | view | raw | blame | history
docs/narr/upgrading.rst 8 ●●●●● patch | view | raw | blame | history
docs/narr/urldispatch.rst 7 ●●●●● patch | view | raw | blame | history
docs/narr/webob.rst 6 ●●●● patch | view | raw | blame | history
docs/quick_tour.rst 2 ●●● patch | view | raw | blame | history
docs/quick_tutorial/forms.rst 5 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/requirements.rst 18 ●●●● patch | view | raw | blame | history
docs/quick_tutorial/tutorial_approach.rst 16 ●●●● patch | view | raw | blame | history
docs/tutorials/modwsgi/index.rst 21 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/distributing.rst 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/installation.rst 93 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/tests.rst 9 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/definingmodels.rst 70 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/distributing.rst 6 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/installation.rst 126 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authentication/tutorial/models/page.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authentication/tutorial/models/user.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authorization/tutorial/models/page.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authorization/tutorial/models/user.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/models/tutorial/models/page.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/models/tutorial/models/user.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/tests/tutorial/models/page.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/tests/tutorial/models/user.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/views/tutorial/models/page.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/views/tutorial/models/user.py 2 ●●● patch | view | raw | blame | history
docs/whatsnew-1.7.rst 34 ●●●● patch | view | raw | blame | history
pyramid/compat.py 10 ●●●● patch | view | raw | blame | history
pyramid/config/security.py 57 ●●●●● patch | view | raw | blame | history
pyramid/config/settings.py 4 ●●●● patch | view | raw | blame | history
pyramid/config/views.py 57 ●●●●● patch | view | raw | blame | history
pyramid/decorator.py 40 ●●●●● patch | view | raw | blame | history
pyramid/interfaces.py 14 ●●●● patch | view | raw | blame | history
pyramid/registry.py 6 ●●●● patch | view | raw | blame | history
pyramid/renderers.py 2 ●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/+dot+coveragerc_tmpl 3 ●●●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/development.ini_tmpl 1 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/pytest.ini_tmpl 3 ●●●●● patch | view | raw | blame | history
pyramid/scaffolds/starter/+dot+coveragerc_tmpl 3 ●●●●● patch | view | raw | blame | history
pyramid/scaffolds/starter/pytest.ini_tmpl 3 ●●●●● patch | view | raw | blame | history
pyramid/scaffolds/tests.py 11 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/zodb/+dot+coveragerc_tmpl 3 ●●●●● patch | view | raw | blame | history
pyramid/scaffolds/zodb/pytest.ini_tmpl 3 ●●●●● patch | view | raw | blame | history
pyramid/session.py 9 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_security.py 21 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_views.py 2 ●●● patch | view | raw | blame | history
pyramid/tests/test_scripts/test_proutes.py 6 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_session.py 14 ●●●● patch | view | raw | blame | history
pyramid/tests/test_tweens.py 73 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_view.py 25 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_viewderivers.py 358 ●●●●● patch | view | raw | blame | history
pyramid/tweens.py 31 ●●●●● patch | view | raw | blame | history
pyramid/view.py 8 ●●●● patch | view | raw | blame | history
pyramid/viewderivers.py 85 ●●●●● patch | view | raw | blame | history
scaffoldtests.sh 2 ●●● patch | view | raw | blame | history
tox.ini 28 ●●●● patch | view | raw | blame | history
.travis.yml
@@ -14,8 +14,6 @@
          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
CHANGES.txt
@@ -4,136 +4,14 @@
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
---------------------
CONTRIBUTORS.txt
@@ -266,3 +266,7 @@
- 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
HISTORY.txt
@@ -1,3 +1,244 @@
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)
================
README.rst
@@ -3,6 +3,7 @@
.. 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/
@@ -25,8 +26,9 @@
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
---------------------------
RELEASING.txt
@@ -6,11 +6,18 @@
  - 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).
@@ -26,13 +33,15 @@
- 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
@@ -52,10 +61,7 @@
  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)::
@@ -70,6 +76,38 @@
  $ 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.
@@ -82,15 +120,28 @@
- 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:
@@ -98,19 +149,20 @@
<<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
```
appveyor.yml
New file
@@ -0,0 +1,12 @@
environment:
  matrix:
    - PYTHON: "C:\\Python35"
      TOXENV: "py35"
install:
  - "%PYTHON%\\python.exe -m pip install tox"
build: off
test_script:
  - "%PYTHON%\\Scripts\\tox.exe"
contributing.md
@@ -25,11 +25,11 @@
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.
docs/Makefile
@@ -12,16 +12,20 @@
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)/*
@@ -90,3 +94,7 @@
    @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."
docs/api/config.rst
@@ -35,6 +35,7 @@
     .. automethod:: set_authentication_policy
     .. automethod:: set_authorization_policy
     .. automethod:: set_default_csrf_options
     .. automethod:: set_default_permission
     .. automethod:: add_permission
@@ -65,6 +66,7 @@
     .. 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
docs/api/index.rst
@@ -1,4 +1,4 @@
.. _html_api_documentation:
.. _api_documentation:
API Documentation
=================
docs/api/interfaces.rst
@@ -59,6 +59,9 @@
  .. autointerface:: IRenderer
     :members:
  .. autointerface:: IRequestFactory
     :members:
  .. autointerface:: IResponseFactory
     :members:
docs/authorintro.rst
@@ -54,7 +54,14 @@
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`
@@ -66,19 +73,16 @@
  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
docs/conf.py
@@ -138,6 +138,7 @@
#     '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',
docs/conventions.rst
@@ -35,7 +35,7 @@
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:
@@ -55,7 +55,7 @@
  .. code-block:: bash
     $ $VENV/bin/py.test tutorial/tests.py -q
     $ $VENV/bin/py.test -q
(See :term:`venv` for the meaning of ``$VENV``)
@@ -64,7 +64,7 @@
  .. 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%``)
@@ -73,7 +73,7 @@
  .. 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
docs/copyright.rst
@@ -1,7 +1,7 @@
Copyright, Trademarks, and Attributions
=======================================
*The Pyramid Web Framework, Version 1.1*
"The Pyramid Web Framework, Version |version|"
by Chris McDonough
@@ -63,7 +63,7 @@
  GitHub.
Cover Designer:
   Hugues Laflamme of `Kemeneur <http://www.kemeneur.com/>`_.
   Hugues Laflamme of Kemeneur.
Used with permission:
@@ -80,8 +80,8 @@
----------------
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
------------------------
@@ -90,7 +90,7 @@
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
----------------------------
@@ -101,4 +101,3 @@
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
docs/designdefense.rst
@@ -131,7 +131,7 @@
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.
@@ -608,7 +608,7 @@
  133KB
pyramid/ (except for ``pyramd/tests`` and ``pyramid/scaffolds``)
pyramid/ (except for ``pyramid/tests`` and ``pyramid/scaffolds``)
  812KB
@@ -665,7 +665,7 @@
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:
@@ -847,9 +847,9 @@
+++++++++
: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
@@ -1006,16 +1006,18 @@
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
@@ -1295,12 +1297,12 @@
    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':
@@ -1320,15 +1322,15 @@
    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>'
@@ -1430,9 +1432,10 @@
        # 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
@@ -1650,10 +1653,11 @@
       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
+++++++++
@@ -1701,5 +1705,6 @@
----------------
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.
docs/glossary.rst
@@ -36,7 +36,7 @@
   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
@@ -51,7 +51,7 @@
     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
@@ -321,18 +321,18 @@
     :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
@@ -346,15 +346,15 @@
     `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
@@ -376,28 +376,27 @@
     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,
@@ -416,7 +415,7 @@
   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
@@ -442,7 +441,7 @@
     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
@@ -488,13 +487,13 @@
   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
@@ -555,7 +554,7 @@
     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
@@ -569,10 +568,10 @@
     :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
@@ -609,7 +608,7 @@
     .. 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`,
@@ -683,7 +682,7 @@
   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`.
@@ -700,8 +699,8 @@
     :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,
@@ -709,14 +708,14 @@
     .. 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
@@ -736,7 +735,7 @@
     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.
@@ -913,7 +912,7 @@
     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
@@ -936,20 +935,19 @@
   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 
@@ -965,7 +963,7 @@
   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
@@ -1008,7 +1006,7 @@
     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)
docs/index.rst
@@ -5,7 +5,7 @@
=========================
: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:
@@ -18,7 +18,7 @@
this application works.
.. _html_getting_started:
.. _getting_started:
Getting Started
===============
@@ -60,9 +60,9 @@
.. 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:
@@ -70,15 +70,18 @@
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
@@ -165,7 +168,7 @@
``p*`` Scripts Documentation
============================
``p*`` scripts included with :app:`Pyramid`:.
``p*`` scripts included with :app:`Pyramid`.
.. toctree::
   :maxdepth: 1
docs/latexindex.rst
@@ -14,11 +14,26 @@
.. 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:
@@ -68,20 +83,6 @@
   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
@@@@@@@@@@@@@@@@@
@@ -89,10 +90,40 @@
   :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
@@@@@@@@@@@@@@@@@@
docs/narr/extconfig.rst
@@ -261,6 +261,7 @@
- :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`
docs/narr/firstapp.rst
@@ -197,7 +197,7 @@
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
docs/narr/hooks.rst
@@ -300,13 +300,38 @@
``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``.
@@ -338,13 +363,42 @@
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
@@ -1593,8 +1647,9 @@
``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``
docs/narr/i18n.rst
@@ -343,7 +343,7 @@
``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
@@ -647,7 +647,7 @@
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::
docs/narr/install.rst
@@ -90,7 +90,7 @@
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.
@@ -99,7 +99,7 @@
   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.
docs/narr/introduction.rst
@@ -221,7 +221,7 @@
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -881,7 +881,7 @@
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
~~~~~~~~~~~~~
@@ -903,7 +903,7 @@
: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::
@@ -967,9 +967,9 @@
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?
docs/narr/introspector.rst
@@ -337,6 +337,31 @@
    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
docs/narr/logging.rst
@@ -292,7 +292,8 @@
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
docs/narr/muchadoabouttraversal.rst
@@ -8,9 +8,7 @@
.. 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.
docs/narr/project.rst
@@ -209,19 +209,19 @@
.. 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
@@ -234,6 +234,26 @@
    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
@@ -584,7 +604,7 @@
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`.
@@ -628,8 +648,8 @@
``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
@@ -647,8 +667,8 @@
``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
@@ -697,21 +717,21 @@
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
@@ -909,10 +929,10 @@
   :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.
docs/narr/renderers.rst
@@ -317,7 +317,7 @@
.. 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.
docs/narr/sessions.rst
@@ -260,19 +260,28 @@
.. 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
@@ -287,15 +296,17 @@
.. 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
@@ -305,7 +316,7 @@
---------------------------------------------
`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.
@@ -396,13 +407,13 @@
.. 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:
@@ -414,41 +425,45 @@
: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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
docs/narr/templates.rst
@@ -448,7 +448,7 @@
.. _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/
docs/narr/upgrading.rst
@@ -128,7 +128,8 @@
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
@@ -215,9 +216,10 @@
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
--------------------------------------------
docs/narr/urldispatch.rst
@@ -271,8 +271,9 @@
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
@@ -1194,7 +1195,7 @@
       __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:
docs/narr/webob.rst
@@ -27,8 +27,8 @@
: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"
@@ -46,7 +46,7 @@
~~~~~~~
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
docs/quick_tour.rst
@@ -699,7 +699,7 @@
.. 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.
docs/quick_tutorial/forms.rst
@@ -1,7 +1,7 @@
.. _qtut_forms:
====================================
18: Forms and Validation With Deform
18: Forms and Validation with Deform
====================================
Schema-driven, autogenerated forms with validation.
@@ -18,9 +18,6 @@
: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
docs/quick_tutorial/requirements.rst
@@ -79,15 +79,15 @@
.. 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:
docs/quick_tutorial/tutorial_approach.rst
@@ -32,14 +32,14 @@
.. 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``
docs/tutorials/modwsgi/index.rst
@@ -18,7 +18,7 @@
   ``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
@@ -29,7 +29,7 @@
#.  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
@@ -44,11 +44,11 @@
#.  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`
@@ -119,9 +119,8 @@
#.  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.
docs/tutorials/wiki/distributing.rst
@@ -36,6 +36,6 @@
``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.
docs/tutorials/wiki/installation.rst
@@ -97,16 +97,17 @@
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
---------------------------------------------------
@@ -127,6 +128,7 @@
.. code-block:: doscon
   c:\> cd pyramidtut
.. _making_a_project:
@@ -208,7 +210,7 @@
   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
----------------------------
@@ -251,21 +253,23 @@
-------------
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:
@@ -291,15 +295,15 @@
.. 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:
@@ -325,6 +329,40 @@
   ===================== 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:
@@ -357,9 +395,9 @@
.. 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.
@@ -386,9 +424,28 @@
- 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
docs/tutorials/wiki/tests.rst
@@ -52,20 +52,21 @@
=================
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:
docs/tutorials/wiki2/definingmodels.rst
@@ -191,49 +191,49 @@
.. 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
docs/tutorials/wiki2/distributing.rst
@@ -35,6 +35,6 @@
``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.
docs/tutorials/wiki2/installation.rst
@@ -97,16 +97,16 @@
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
@@ -270,21 +270,23 @@
-------------
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:
@@ -310,15 +312,15 @@
.. 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:
@@ -338,19 +340,51 @@
   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:
@@ -396,30 +430,30 @@
.. 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
@@ -451,7 +485,9 @@
   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.
@@ -463,7 +499,7 @@
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
@@ -494,19 +530,7 @@
   :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/
docs/tutorials/wiki2/src/authentication/tutorial/models/page.py
@@ -14,7 +14,7 @@
    __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')
docs/tutorials/wiki2/src/authentication/tutorial/models/user.py
@@ -23,7 +23,7 @@
    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
docs/tutorials/wiki2/src/authorization/tutorial/models/page.py
@@ -14,7 +14,7 @@
    __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')
docs/tutorials/wiki2/src/authorization/tutorial/models/user.py
@@ -23,7 +23,7 @@
    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
docs/tutorials/wiki2/src/models/tutorial/models/page.py
@@ -14,7 +14,7 @@
    __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')
docs/tutorials/wiki2/src/models/tutorial/models/user.py
@@ -23,7 +23,7 @@
    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
docs/tutorials/wiki2/src/tests/tutorial/models/page.py
@@ -14,7 +14,7 @@
    __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')
docs/tutorials/wiki2/src/tests/tutorial/models/user.py
@@ -23,7 +23,7 @@
    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
docs/tutorials/wiki2/src/views/tutorial/models/page.py
@@ -14,7 +14,7 @@
    __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')
docs/tutorials/wiki2/src/views/tutorial/models/user.py
@@ -23,7 +23,7 @@
    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
docs/whatsnew-1.7.rst
@@ -32,6 +32,11 @@
  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
-----------------
@@ -42,14 +47,6 @@
  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
@@ -59,6 +56,17 @@
  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
@@ -96,6 +104,11 @@
  ``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'``.
@@ -111,6 +124,11 @@
  :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
------------
pyramid/compat.py
@@ -3,10 +3,7 @@
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__
@@ -214,11 +211,6 @@
    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
pyramid/config/security.py
@@ -1,11 +1,15 @@
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
@@ -138,7 +142,6 @@
        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
@@ -159,3 +162,55 @@
        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)
pyramid/config/settings.py
@@ -122,8 +122,6 @@
                                             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
@@ -138,7 +136,6 @@
            '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,
@@ -151,7 +148,6 @@
            '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,
        }
pyramid/config/views.py
@@ -13,7 +13,6 @@
from zope.interface.interfaces import IInterface
from pyramid.interfaces import (
    IDefaultPermission,
    IException,
    IExceptionViewClassifier,
    IMultiView,
@@ -371,24 +370,24 @@
          .. 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.
@@ -878,11 +877,6 @@
                        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']
@@ -1229,7 +1223,6 @@
        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),
@@ -1245,6 +1238,16 @@
                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):
        """
@@ -1427,7 +1430,10 @@
        .. 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'
@@ -1455,6 +1461,7 @@
            match_param=match_param,
            route_name=route_name,
            permission=NO_PERMISSION_REQUIRED,
            require_csrf=False,
            attr=attr,
            renderer=renderer,
            )
@@ -1539,7 +1546,10 @@
        .. 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'
@@ -1567,6 +1577,7 @@
            match_param=match_param,
            route_name=route_name,
            permission=NO_PERMISSION_REQUIRED,
            require_csrf=False,
            )
        settings.update(view_options)
        if append_slash:
pyramid/decorator.py
@@ -1,4 +1,4 @@
import functools
from functools import update_wrapper
class reify(object):
@@ -8,28 +8,36 @@
    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:
pyramid/interfaces.py
@@ -916,6 +916,16 @@
    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 """
@@ -1214,9 +1224,9 @@
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')
pyramid/registry.py
@@ -255,9 +255,13 @@
    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
pyramid/renderers.py
@@ -294,7 +294,7 @@
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.
pyramid/scaffolds/alchemy/+dot+coveragerc_tmpl
New file
@@ -0,0 +1,3 @@
[run]
source = {{package}}
omit = {{package}}/test*
pyramid/scaffolds/alchemy/development.ini_tmpl
@@ -13,7 +13,6 @@
pyramid.default_locale_name = en
pyramid.includes =
    pyramid_debugtoolbar
    pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite
pyramid/scaffolds/alchemy/pytest.ini_tmpl
New file
@@ -0,0 +1,3 @@
[pytest]
testpaths = {{package}}
python_files = *.py
pyramid/scaffolds/starter/+dot+coveragerc_tmpl
New file
@@ -0,0 +1,3 @@
[run]
source = {{package}}
omit = {{package}}/test*
pyramid/scaffolds/starter/pytest.ini_tmpl
New file
@@ -0,0 +1,3 @@
[pytest]
testpaths = {{package}}
python_files = *.py
pyramid/scaffolds/tests.py
@@ -29,19 +29,18 @@
            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)):
pyramid/scaffolds/zodb/+dot+coveragerc_tmpl
New file
@@ -0,0 +1,3 @@
[run]
source = {{package}}
omit = {{package}}/test*
pyramid/scaffolds/zodb/pytest.ini_tmpl
New file
@@ -0,0 +1,3 @@
[pytest]
testpaths = {{package}}
python_files = *.py
pyramid/session.py
@@ -109,7 +109,6 @@
    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
@@ -182,7 +181,7 @@
                    "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)
@@ -233,16 +232,18 @@
       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()
pyramid/tests/test_config/test_security.py
@@ -98,3 +98,24 @@
        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'])
pyramid/tests/test_config/test_views.py
@@ -3093,7 +3093,7 @@
        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()
pyramid/tests/test_scripts/test_proutes.py
@@ -1,3 +1,4 @@
import os
import unittest
from pyramid.tests.test_scripts import dummy
@@ -396,7 +397,8 @@
        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'
@@ -413,7 +415,7 @@
        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/',  '*'],
        ]
pyramid/tests/test_session.py
@@ -721,7 +721,7 @@
        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))
@@ -730,7 +730,7 @@
        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 = {}
@@ -740,7 +740,7 @@
        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"],
@@ -751,7 +751,7 @@
        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))
@@ -761,7 +761,7 @@
        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)
@@ -780,7 +780,7 @@
        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)
@@ -791,7 +791,7 @@
        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)
pyramid/tests/test_tweens.py
New file
@@ -0,0 +1,73 @@
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
pyramid/tests/test_view.py
@@ -805,6 +805,31 @@
        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']
pyramid/tests/test_viewderivers.py
@@ -628,6 +628,63 @@
        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()
@@ -1090,73 +1147,6 @@
        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):
@@ -1167,68 +1157,42 @@
        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
@@ -1237,12 +1201,75 @@
        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
@@ -1251,20 +1278,35 @@
        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)
@@ -1277,25 +1319,69 @@
        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):
@@ -1554,7 +1640,6 @@
        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
@@ -1618,6 +1703,7 @@
            environ = {}
        self.environ = environ
        self.params = {}
        self.POST = {}
        self.cookies = {}
        self.headers = {}
        self.response = DummyResponse()
pyramid/tweens.py
@@ -1,5 +1,7 @@
import sys
from pyramid.compat import reraise
from pyramid.exceptions import PredicateMismatch
from pyramid.interfaces import (
    IExceptionViewClassifier,
    IRequest,
@@ -38,17 +40,26 @@
            # 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
pyramid/view.py
@@ -21,6 +21,7 @@
from pyramid.httpexceptions import (
    HTTPFound,
    HTTPNotFound,
    default_exceptionresponse_view,
    )
@@ -589,8 +590,9 @@
        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
@@ -623,4 +625,6 @@
                secure=secure,
                request_iface=request_iface.combined,
                )
            if response is None:
                raise HTTPNotFound
            return response
pyramid/viewderivers.py
@@ -15,6 +15,8 @@
from pyramid.interfaces import (
    IAuthenticationPolicy,
    IAuthorizationPolicy,
    IDefaultCSRFOptions,
    IDefaultPermission,
    IDebugLogger,
    IResponse,
    IViewMapper,
@@ -22,7 +24,6 @@
    )
from pyramid.compat import (
    string_types,
    is_bound_method,
    is_unbound_method,
    )
@@ -38,10 +39,6 @@
    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
@@ -276,7 +273,9 @@
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
@@ -292,6 +291,12 @@
            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)
@@ -310,12 +315,20 @@
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:
@@ -464,40 +477,38 @@
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
scaffoldtests.sh
@@ -1,3 +1,3 @@
#!/bin/bash
tox -e{py26,py27,py32,py33,py34,pypy,pypy3}-scaffolds,
tox -e{py27,py33,py34,pypy}-scaffolds,
tox.ini
@@ -1,6 +1,6 @@
[tox]
envlist =
    py27,py33,py34,py35,pypy,pypy3,
    py27,py33,py34,py35,pypy,
    docs,pep8,
    {py2,py3}-cover,coverage,
@@ -13,7 +13,6 @@
    py34: python3.4
    py35: python3.5
    pypy: pypy
    pypy3: pypy3
    py2: python2.7
    py3: python3.5
@@ -27,14 +26,8 @@
    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
@@ -45,14 +38,14 @@
    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
@@ -69,7 +62,14 @@
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