Chris McDonough
2011-08-11 995466c6bc0da04f50d2db83af653362a0dadd6f
fix merge conflicts
4 files deleted
11 files added
176 files modified
9627 ■■■■ changed files
CHANGES.txt 345 ●●●●● patch | view | raw | blame | history
CONTRIBUTORS.txt 4 ●●●● patch | view | raw | blame | history
LICENSE.txt 53 ●●●●● patch | view | raw | blame | history
RELEASING.txt 33 ●●●●● patch | view | raw | blame | history
TODO.txt 64 ●●●● patch | view | raw | blame | history
docs/api.rst 2 ●●●●● patch | view | raw | blame | history
docs/api/config.rst 16 ●●●●● patch | view | raw | blame | history
docs/api/interfaces.rst 5 ●●●●● patch | view | raw | blame | history
docs/api/paster.rst 14 ●●●●● patch | view | raw | blame | history
docs/api/renderers.rst 9 ●●●●● patch | view | raw | blame | history
docs/api/request.rst 21 ●●●● patch | view | raw | blame | history
docs/api/response.rst 5 ●●●●● patch | view | raw | blame | history
docs/api/scripting.rst 2 ●●●●● patch | view | raw | blame | history
docs/api/security.rst 2 ●●●●● patch | view | raw | blame | history
docs/api/static.rst 11 ●●●●● patch | view | raw | blame | history
docs/api/tweens.rst 25 ●●●●● patch | view | raw | blame | history
docs/authorintro.rst 6 ●●●●● patch | view | raw | blame | history
docs/conf.py 2 ●●● patch | view | raw | blame | history
docs/copyright.rst 8 ●●●● patch | view | raw | blame | history
docs/designdefense.rst 75 ●●●● patch | view | raw | blame | history
docs/glossary.rst 32 ●●●●● patch | view | raw | blame | history
docs/index.rst 17 ●●●● patch | view | raw | blame | history
docs/latexindex.rst 15 ●●●● patch | view | raw | blame | history
docs/make_book 2 ●●● patch | view | raw | blame | history
docs/make_epub 2 ●●●●● patch | view | raw | blame | history
docs/make_pdf 4 ●●●● patch | view | raw | blame | history
docs/narr/MyProject/development.ini 15 ●●●● patch | view | raw | blame | history
docs/narr/MyProject/myproject/templates/mytemplate.pt 14 ●●●● patch | view | raw | blame | history
docs/narr/MyProject/production.ini 13 ●●●● patch | view | raw | blame | history
docs/narr/MyProject/setup.py 2 ●●● patch | view | raw | blame | history
docs/narr/advconfig.rst 9 ●●●● patch | view | raw | blame | history
docs/narr/assets.rst 34 ●●●●● patch | view | raw | blame | history
docs/narr/commandline.rst 560 ●●●●● patch | view | raw | blame | history
docs/narr/configuration.rst 73 ●●●● patch | view | raw | blame | history
docs/narr/environment.rst 171 ●●●●● patch | view | raw | blame | history
docs/narr/firstapp.rst 13 ●●●● patch | view | raw | blame | history
docs/narr/hooks.rst 301 ●●●●● patch | view | raw | blame | history
docs/narr/hybrid.rst 32 ●●●● patch | view | raw | blame | history
docs/narr/i18n.rst 41 ●●●● patch | view | raw | blame | history
docs/narr/install.rst 41 ●●●●● patch | view | raw | blame | history
docs/narr/introduction.rst 85 ●●●●● patch | view | raw | blame | history
docs/narr/muchadoabouttraversal.rst 15 ●●●● patch | view | raw | blame | history
docs/narr/project-debug.png patch | view | raw | blame | history
docs/narr/project.png patch | view | raw | blame | history
docs/narr/project.rst 255 ●●●● patch | view | raw | blame | history
docs/narr/renderers.rst 28 ●●●●● patch | view | raw | blame | history
docs/narr/resources.rst 18 ●●●●● patch | view | raw | blame | history
docs/narr/router.rst 1 ●●●● patch | view | raw | blame | history
docs/narr/security.rst 19 ●●●●● patch | view | raw | blame | history
docs/narr/sessions.rst 26 ●●●●● patch | view | raw | blame | history
docs/narr/startup.rst 30 ●●●●● patch | view | raw | blame | history
docs/narr/templates.rst 30 ●●●● patch | view | raw | blame | history
docs/narr/traversal.rst 97 ●●●●● patch | view | raw | blame | history
docs/narr/urldispatch.rst 293 ●●●●● patch | view | raw | blame | history
docs/narr/vhosting.rst 3 ●●●●● patch | view | raw | blame | history
docs/narr/viewconfig.rst 394 ●●●●● patch | view | raw | blame | history
docs/narr/views.rst 237 ●●●● patch | view | raw | blame | history
docs/narr/webob.rst 90 ●●●●● patch | view | raw | blame | history
docs/narr/zca.rst 1 ●●●● patch | view | raw | blame | history
docs/remake 1 ●●●● patch | view | raw | blame | history
docs/tutorials/modwsgi/index.rst 3 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/authorization/development.ini 23 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/authorization/production.ini 21 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/authorization/setup.py 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt 17 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/basiclayout/development.ini 23 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/basiclayout/production.ini 21 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/basiclayout/setup.py 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt 17 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/models/development.ini 23 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/models/production.ini 21 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/models/setup.py 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/models/tutorial/static/pylons.css 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt 17 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/tests/development.ini 23 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/tests/production.ini 21 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/tests/setup.py 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/tests/tutorial/static/pylons.css 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt 17 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/views/development.ini 23 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/views/production.ini 21 ●●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/views/setup.py 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/views/tutorial/static/pylons.css 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt 17 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/authorization.rst 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/basiclayout.rst 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authorization/development.ini 12 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authorization/production.ini 12 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authorization/tutorial/models.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt 17 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/basiclayout/development.ini 12 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/basiclayout/production.ini 12 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/basiclayout/tutorial/models.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt 17 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/models/development.ini 12 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/models/production.ini 12 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/models/tutorial/models.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/models/tutorial/static/pylons.css 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt 17 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/tests/development.ini 12 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/tests/production.ini 12 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/tests/tutorial/models.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt 17 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/views/development.ini 12 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/views/production.ini 12 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/views/tutorial/models.py 2 ●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/views/tutorial/static/pylons.css 4 ●●●● patch | view | raw | blame | history
docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt 17 ●●●● patch | view | raw | blame | history
docs/whatsnew-1.0.rst 4 ●●●● patch | view | raw | blame | history
docs/whatsnew-1.1.rst 255 ●●●● patch | view | raw | blame | history
pyramid/compat.py 8 ●●●●● patch | view | raw | blame | history
pyramid/compat/__init__.py 157 ●●●●● patch | view | raw | blame | history
pyramid/config.py 404 ●●●● patch | view | raw | blame | history
pyramid/events.py 53 ●●●●● patch | view | raw | blame | history
pyramid/httpexceptions.py 5 ●●●●● patch | view | raw | blame | history
pyramid/i18n.py 5 ●●●●● patch | view | raw | blame | history
pyramid/interfaces.py 37 ●●●● patch | view | raw | blame | history
pyramid/log.py 16 ●●●●● patch | view | raw | blame | history
pyramid/paster.py 370 ●●●● patch | view | raw | blame | history
pyramid/renderers.py 61 ●●●●● patch | view | raw | blame | history
pyramid/request.py 21 ●●●● patch | view | raw | blame | history
pyramid/response.py 59 ●●●●● patch | view | raw | blame | history
pyramid/router.py 254 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/+package+/models.py 2 ●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/+package+/static/pylons.css 2 ●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/+package+/templates/model.pt_tmpl 18 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/+package+/templates/root.pt_tmpl 18 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/development.ini_tmpl 22 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/production.ini_tmpl 20 ●●●●● patch | view | raw | blame | history
pyramid/scaffolds/alchemy/setup.py_tmpl 3 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/routesalchemy/+package+/models.py 2 ●●● patch | view | raw | blame | history
pyramid/scaffolds/routesalchemy/+package+/static/pylons.css 2 ●●● patch | view | raw | blame | history
pyramid/scaffolds/routesalchemy/+package+/templates/mytemplate.pt_tmpl 18 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/routesalchemy/development.ini_tmpl 22 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/routesalchemy/production.ini_tmpl 20 ●●●●● patch | view | raw | blame | history
pyramid/scaffolds/routesalchemy/setup.py_tmpl 3 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/starter/+package+/static/pylons.css 2 ●●● patch | view | raw | blame | history
pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl 18 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/starter/development.ini_tmpl 15 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/starter/production.ini_tmpl 13 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/starter/setup.py_tmpl 2 ●●● patch | view | raw | blame | history
pyramid/scaffolds/tests.py 43 ●●●●● patch | view | raw | blame | history
pyramid/scaffolds/zodb/+package+/static/pylons.css 2 ●●● patch | view | raw | blame | history
pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl 18 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/zodb/development.ini_tmpl 24 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/zodb/production.ini_tmpl 22 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/zodb/setup.py_tmpl 4 ●●●● patch | view | raw | blame | history
pyramid/scripting.py 95 ●●●●● patch | view | raw | blame | history
pyramid/security.py 2 ●●●●● patch | view | raw | blame | history
pyramid/settings.py 39 ●●●●● patch | view | raw | blame | history
pyramid/static.py 21 ●●●● patch | view | raw | blame | history
pyramid/tests/defpermbugapp/__init__.py 3 ●●●● patch | view | raw | blame | history
pyramid/tests/grokkedapp/__init__.py 34 ●●●●● patch | view | raw | blame | history
pyramid/tests/grokkedapp/another.py 24 ●●●●● patch | view | raw | blame | history
pyramid/tests/grokkedapp/pod/notinit.py 3 ●●●● patch | view | raw | blame | history
pyramid/tests/grokkedapp/subpackage/__init__.py 3 ●●●● patch | view | raw | blame | history
pyramid/tests/grokkedapp/subpackage/notinit.py 3 ●●●● patch | view | raw | blame | history
pyramid/tests/grokkedapp/subpackage/subsubpackage/__init__.py 3 ●●●● patch | view | raw | blame | history
pyramid/tests/restbugapp/views.py 2 ●●● patch | view | raw | blame | history
pyramid/tests/test_compat.py 9 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_config.py 738 ●●●● patch | view | raw | blame | history
pyramid/tests/test_events.py 30 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_httpexceptions.py 11 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_i18n.py 19 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_integration.py 6 ●●●● patch | view | raw | blame | history
pyramid/tests/test_log.py 16 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_paster.py 680 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_renderers.py 63 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_request.py 34 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_response.py 64 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_router.py 118 ●●●● patch | view | raw | blame | history
pyramid/tests/test_scripting.py 114 ●●●● patch | view | raw | blame | history
pyramid/tests/test_settings.py 283 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_static.py 60 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_tweens.py 324 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_util.py 73 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_view.py 101 ●●●● patch | view | raw | blame | history
pyramid/tests/venusianapp/__init__.py 14 ●●●●● patch | view | raw | blame | history
pyramid/tweens.py 198 ●●●●● patch | view | raw | blame | history
pyramid/url.py 2 ●●● patch | view | raw | blame | history
pyramid/urldispatch.py 2 ●●●●● patch | view | raw | blame | history
pyramid/util.py 63 ●●●●● patch | view | raw | blame | history
pyramid/view.py 37 ●●●● patch | view | raw | blame | history
pyramid/wsgi.py 2 ●●● patch | view | raw | blame | history
setup.py 3 ●●●● patch | view | raw | blame | history
tox.ini 2 ●●● patch | view | raw | blame | history
CHANGES.txt
@@ -4,10 +4,324 @@
Features
--------
- New request attribute: ``json``. If the request's ``content_type`` is
  ``application/json``, this attribute will contain the JSON-decoded
  variant of the request body.  If the request's ``content_type`` is not
  ``application/json``, this attribute will be ``None``.
- Added a ``pyramid.security.NO_PERMISSION_REQUIRED`` constant for use in
  ``permission=`` statements to view configuration.  This constant has a
  value of the string ``__no_permission_required__``.  This string value was
  previously referred to in documentation; now the documentation uses the
  constant.
- Added a decorator-based way to configure a response adapter:
  ``pyramid.response.response_adapter``.  This decorator has the same use as
  ``pyramid.config.Configurator.add_response_adapter`` but it's declarative.
- The ``pyramid.events.BeforeRender`` event now has an attribute named
  ``rendering_val``.  This can be used to introspect the value returned by a
  view in a BeforeRender subscriber.
- New configurator directive: ``pyramid.config.Configurator.add_tween``.
  This directive adds a "tween".  A "tween" is used to wrap the Pyramid
  router's primary request handling function.  This is a feature may be used
  by Pyramid framework extensions, to provide, for example, view timing
  support and as a convenient place to hang bookkeeping code.
  Tweens are further described in the narrative docs section in the Hooks
  chapter, named "Registering Tweens".
- New paster command ``paster ptweens``, which prints the current "tween"
  configuration for an application.  See the section entitled "Displaying
  Tweens" in the Command-Line Pyramid chapter of the narrative documentation
  for more info.
- The Pyramid debug logger now uses the standard logging configuration
  (usually set up by Paste as part of startup).  This means that output from
  e.g. ``debug_notfound``, ``debug_authorization``, etc. will go to the
  normal logging channels.  The logger name of the debug logger will be the
  package name of the *caller* of the Configurator's constructor.
- A new attribute is available on request objects: ``exc_info``.  Its value
  will be ``None`` until an exception is caught by the Pyramid router, after
  which it will be the result of ``sys.exc_info()``.
Internal
--------
- The Pyramid "exception view" machinery is now implemented as a "tween"
  (``pyramid.tweens.excview_tween_factory``).
Deprecations
------------
- All Pyramid-related deployment settings (e.g. ``debug_all``,
  ``debug_notfound``) are now meant to be prefixed with the prefix
  ``pyramid.``.  For example: ``debug_all`` -> ``pyramid.debug_all``.  The
  old non-prefixed settings will continue to work indefinitely but supplying
  them may print a deprecation warning.  All scaffolds and tutorials have
  been changed to use prefixed settings.
Backwards Incompatibilities
---------------------------
- If a string is passed as the ``debug_logger`` parameter to a Configurator,
  that string is considered to be the name of a global Python logger rather
  than a dotted name to an instance of a logger.
Documentation
-------------
- Added a new module to the API docs: ``pyramid.tweens``.
- Added a "Registering Tweens" section to the "Hooks" narrative chapter.
- Added a "Displaying Tweens" section to the "Command-Line Pyramid" narrative
  chapter.
Bug Fixes
---------
- Fixed an issue with the default renderer not working at certain times.  See
  https://github.com/Pylons/pyramid/issues/249
1.1 (2011-07-22)
================
Features
--------
- Added the ``pyramid.renderers.null_renderer`` object as an API.  The null
  renderer is an object that can be used in advanced integration cases as
  input to the view configuration ``renderer=`` argument.  When the null
  renderer is used as a view renderer argument, Pyramid avoids converting the
  view callable result into a Response object.  This is useful if you want to
  reuse the view configuration and lookup machinery outside the context of
  its use by the Pyramid router.  This feature was added for consumption by
  the ``pyramid_rpc`` package, which uses view configuration and lookup
  outside the context of a router in exactly this way.  ``pyramid_rpc`` has
  been broken under 1.1 since 1.1b1; adding it allows us to make it work
  again.
- Change all scaffolding templates that point to docs.pylonsproject.org to
  use ``/projects/pyramid/current`` rather than ``/projects/pyramid/dev``.
Internals
---------
- Remove ``compat`` code that served only the purpose of providing backwards
  compatibility with Python 2.4.
- Add a deprecation warning for non-API function
  ``pyramid.renderers.renderer_from_name`` which has seen use in the wild.
- Add a ``clone`` method to ``pyramid.renderers.RendererHelper`` for use by
  the ``pyramid.view.view_config`` decorator.
Documentation
-------------
- Fixed two typos in wiki2 (SQLA + URL Dispatch) tutorial.
- Reordered chapters in narrative section for better new user friendliness.
- Added more indexing markers to sections in documentation.
1.1b4 (2011-07-18)
==================
Documentation
-------------
- Added a section entitled "Writing a Script" to the "Command-Line Pyramid"
  chapter.
Backwards Incompatibilities
---------------------------
- We added the ``pyramid.scripting.make_request`` API too hastily in 1.1b3.
  It has been removed.  Sorry for any inconvenience.  Use the
  ``pyramid.request.Request.blank`` API instead.
Features
--------
- The ``paster pshell``, ``paster pviews``, and ``paster proutes`` commands
  each now under the hood uses ``pyramid.paster.bootstrap``, which makes it
  possible to supply an ``.ini`` file without naming the "right" section in
  the file that points at the actual Pyramid application.  Instead, you can
  generally just run ``paster {pshell|proutes|pviews} development.ini`` and
  it will do mostly the right thing.
Bug Fixes
---------
- Omit custom environ variables when rendering a custom exception template in
  ``pyramid.httpexceptions.WSGIHTTPException._set_default_attrs``;
  stringifying thse may trigger code that should not be executed; see
  https://github.com/Pylons/pyramid/issues/239
1.1b3 (2011-07-15)
==================
Features
--------
- Fix corner case to ease semifunctional testing of views: create a new
  rendererinfo to clear out old registry on a rescan.  See
  https://github.com/Pylons/pyramid/pull/234.
- New API class: ``pyramid.static.static_view``.  This supersedes the
  deprecated ``pyramid.view.static`` class.  ``pyramid.static.static_view``
  by default serves up documents as the result of the request's
  ``path_info``, attribute rather than it's ``subpath`` attribute (the
  inverse was true of ``pyramid.view.static``, and still is).
  ``pyramid.static.static_view`` exposes a ``use_subpath`` flag for use when
  you want the static view to behave like the older deprecated version.
- A new API function ``pyramid.paster.bootstrap`` has been added to make
  writing scripts that bootstrap a Pyramid environment easier, e.g.::
      from pyramid.paster import bootstrap
      info = bootstrap('/path/to/my/development.ini')
      request = info['request']
      print request.route_url('myroute')
- A new API function ``pyramid.scripting.prepare`` has been added.  It is a
  lower-level analogue of ``pyramid.paster.boostrap`` that accepts a request
  and a registry instead of a config file argument, and is used for the same
  purpose::
      from pyramid.scripting import prepare
      info = prepare(registry=myregistry)
      request = info['request']
      print request.route_url('myroute')
- A new API function ``pyramid.scripting.make_request`` has been added.  The
  resulting request will have a ``registry`` attribute.  It is meant to be
  used in conjunction with ``pyramid.scripting.prepare`` and/or
  ``pyramid.paster.bootstrap`` (both of which accept a request as an
  argument)::
      from pyramid.scripting import make_request
      request = make_request('/')
- New API attribute ``pyramid.config.global_registries`` is an iterable
  object that contains references to every Pyramid registry loaded into the
  current process via ``pyramid.config.Configurator.make_app``.  It also has
  a ``last`` attribute containing the last registry loaded.  This is used by
  the scripting machinery, and is available for introspection.
Deprecations
------------
- The ``pyramid.view.static`` class has been deprecated in favor of the newer
  ``pyramid.static.static_view`` class.  A deprecation warning is raised when
  it is used.  You should replace it with a reference to
  ``pyramid.static.static_view`` with the ``use_subpath=True`` argument.
Bug Fixes
---------
- Without a mo-file loaded for the combination of domain/locale,
  ``pyramid.i18n.Localizer.pluralize`` run using that domain/locale
  combination raised an inscrutable "translations object has no attr
  'plural'" error.  Now, instead it "works" (it uses a germanic pluralization
  by default).  It's nonsensical to try to pluralize something without
  translations for that locale/domain available, but this behavior matches
  the behavior of ``pyramid.i18n.Localizer.translate`` so it's at least
  consistent; see https://github.com/Pylons/pyramid/issues/235.
1.1b2 (2011-07-13)
==================
Features
--------
- New environment setting ``PYRAMID_PREVENT_HTTP_CACHE`` and new
  configuration file value ``prevent_http_cache``.  These are synomymous and
  allow you to prevent HTTP cache headers from being set by Pyramid's
  ``http_cache`` machinery globally in a process.  see the "Influencing HTTP
  Caching" section of the "View Configuration" narrative chapter and the
  detailed documentation for this setting in the "Environment Variables and
  Configuration Settings" narrative chapter.
Behavior Changes
----------------
- Previously, If a ``BeforeRender`` event subscriber added a value via the
  ``__setitem__`` or ``update`` methods of the event object with a key that
  already existed in the renderer globals dictionary, a ``KeyError`` was
  raised.  With the deprecation of the "add_renderer_globals" feature of the
  configurator, there was no way to override an existing value in the
  renderer globals dictionary that already existed.  Now, the event object
  will overwrite an older value that is already in the globals dictionary
  when its ``__setitem__`` or ``update`` is called (as well as the new
  ``setdefault`` method), just like a plain old dictionary.  As a result, for
  maximum interoperability with other third-party subscribers, if you write
  an event subscriber meant to be used as a BeforeRender subscriber, your
  subscriber code will now need to (using ``.get`` or ``__contains__`` of the
  event object) ensure no value already exists in the renderer globals
  dictionary before setting an overriding value.
Bug Fixes
---------
- The ``Configurator.add_route`` method allowed two routes with the same
  route to be added without an intermediate ``config.commit()``.  If you now
  receive a ``ConfigurationError`` at startup time that appears to be
  ``add_route`` related, you'll need to either a) ensure that all of your
  route names are unique or b) call ``config.commit()`` before adding a
  second route with the name of a previously added name or c) use a
  Configurator that works in ``autocommit`` mode.
- The ``pyramid_routesalchemy`` and ``pyramid_alchemy`` scaffolds
  inappropriately used ``DBSession.rollback()`` instead of
  ``transaction.abort()`` in one place.
- We now clear ``request.response`` before we invoke an exception view; an
  exception view will be working with a request.response that has not been
  touched by any code prior to the exception.
- Views associated with routes with spaces in the route name may not have
  been looked up correctly when using Pyramid with ``zope.interface`` 3.6.4
  and better.  See https://github.com/Pylons/pyramid/issues/232.
Documentation
-------------
- Wiki2 (SQLAlchemy + URL Dispatch) tutorial ``models.initialize_sql`` didn't
  match the ``pyramid_routesalchemy`` scaffold function of the same name; it
  didn't get synchronized when it was changed in the scaffold.
- New documentation section in View Configuration narrative chapter:
  "Influencing HTTP Caching".
1.1b1 (2011-07-10)
==================
Features
--------
- It is now possible to invoke ``paster pshell`` even if the paste ini file
  section name pointed to in its argument is not actually a Pyramid WSGI
  application.  The shell will work in a degraded mode, and will warn the
  user.  See "The Interactive Shell" in the "Creating a Pyramid Project"
  narrative documentation section.
- ``paster pshell`` now offers more built-in global variables by default
  (including ``app`` and ``settings``).  See "The Interactive Shell" in the
  "Creating a Pyramid Project" narrative documentation section.
- It is now possible to add a ``[pshell]`` section to your application's .ini
  configuration file, which influences the global names available to a pshell
  session.  See "Extending the Shell" in the "Creating a Pyramid Project"
  narrative documentation chapter.
- The ``config.scan`` method has grown a ``**kw`` argument.  ``kw`` argument
  represents a set of keyword arguments to pass to the Venusian ``Scanner``
  object created by Pyramid.  (See the Venusian documentation for more
  information about ``Scanner``).
- New request property: ``json_body``. This property will return the
  JSON-decoded variant of the request body.  If the request body is not
  well-formed JSON, this property will raise an exception.
- A new value ``http_cache`` can be used as a view configuration
  parameter.
@@ -60,6 +374,29 @@
  to only influence ``Cache-Control`` headers, pass a tuple as ``http_cache``
  with the first element of ``None``, e.g.: ``(None, {'public':True})``.
Bug Fixes
---------
- Framework wrappers of the original view (such as http_cached and so on)
  relied on being able to trust that the response they were receiving was an
  IResponse.  It wasn't always, because the response was resolved by the
  router instead of early in the view wrapping process.  This has been fixed.
Documentation
-------------
- Added a section in the "Webob" chapter named "Dealing With A JSON-Encoded
  Request Body" (usage of ``request.json_body``).
Behavior Changes
----------------
- The ``paster pshell``, ``paster proutes``, and ``paster pviews`` commands
  now take a single argument in the form ``/path/to/config.ini#sectionname``
  rather than the previous 2-argument spelling ``/path/to/config.ini
  sectionname``.  ``#sectionname`` may be omitted, in which case ``#main`` is
  assumed.
1.1a4 (2011-07-01)
==================
CONTRIBUTORS.txt
@@ -144,3 +144,7 @@
- Christoph Zwerschke, 2011/06/07
- Atsushi Odagiri, 2011/07/02
- Shane Hathaway, 2011/07/22
- Manuel Hermann, 2011/07/11
LICENSE.txt
@@ -98,59 +98,6 @@
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
    DAMAGE.
Portions of the code in Pyramid are supplied under the Python Software
Foundation License version 2 (headers within individiual files indicate that
these portions are so licensed):
  PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
  --------------------------------------------
  1. This LICENSE AGREEMENT is between the Python Software Foundation
  ("PSF"), and the Individual or Organization ("Licensee") accessing and
  otherwise using this software ("Python") in source or binary form and
  its associated documentation.
  2. Subject to the terms and conditions of this License Agreement, PSF
  hereby grants Licensee a nonexclusive, royalty-free, world-wide
  license to reproduce, analyze, test, perform and/or display publicly,
  prepare derivative works, distribute, and otherwise use Python
  alone or in any derivative version, provided, however, that PSF's
  License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
  2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation;
  All Rights Reserved" are retained in Python alone or in any derivative
  version prepared by Licensee.
  3. In the event Licensee prepares a derivative work that is based on
  or incorporates Python or any part thereof, and wants to make
  the derivative work available to others as provided herein, then
  Licensee hereby agrees to include in any such work a brief summary of
  the changes made to Python.
  4. PSF is making Python available to Licensee on an "AS IS"
  basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
  IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
  DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
  FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
  INFRINGE ANY THIRD PARTY RIGHTS.
  5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
  FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
  A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
  OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
  6. This License Agreement will automatically terminate upon a material
  breach of its terms and conditions.
  7. Nothing in this License Agreement shall be deemed to create any
  relationship of agency, partnership, or joint venture between PSF and
  Licensee.  This License Agreement does not grant permission to use PSF
  trademarks or trade name in a trademark sense to endorse or promote
  products or services of Licensee, or any third party.
  8. By copying, installing or otherwise using Python, Licensee
  agrees to be bound by the terms and conditions of this License
  Agreement.
The documentation portion of Pyramid (the rendered contents of the
"docs" directory of a software distribution or checkout) is supplied
under the Creative Commons Attribution-Noncommercial-Share Alike 3.0
RELEASING.txt
@@ -1,7 +1,13 @@
Releasing Pyramid
=================
- Make sure all unit tests pass and statement coverage is at 100%::
- git pull
- Do platform test via tox:
  $ tox
- Make sure statement coverage is at 100%::
  $ python setup.py nosetests --with-coverage
@@ -48,4 +54,29 @@
- Announce to Twitter.
Announcement template
----------------------
Pyramid 1.1.X has been released.
Here are the changes:
<<changes>>
A "What's New In Pyramid 1.1" document exists at
http://docs.pylonsproject.org/projects/pyramid/1.1/whatsnew-1.1.html .
You will be able to see the 1.1 release documentation (across all
alphas and betas, as well as when it eventually gets to final release)
at http://docs.pylonsproject.org/projects/pyramid/1.1/ .
You can install it via PyPI:
  easy_install Pyramid==1.1a4
Enjoy, and please report any issues you find to the issue tracker at
https://github.com/Pylons/pyramid/issues
Thanks!
- C
TODO.txt
@@ -1,34 +1,63 @@
Pyramid TODOs
=============
Must-Have
---------
- Github issues fixes.
Should-Have
-----------
- BeforeRender event subclasses dict but implements a bunch of shit.  Its
  repr is currently broken (it always shows empty).  Decide what to do.
- Replace weberror for email-out in all production.ini.
- Come up with an analogue of repoze.zodbconn that doesn't require a closer
  in the pipeline and use it in the ZODB scaffold and tutorial.
- Deprecate pyramid.security.view_execution_permitted (it only works for
  traversal).
- Make "localizer" a property of request (instead of requiring
  "get_localizer(request)"?
  "get_localizer(request)"
- Investigate mod_wsgi tutorial to make sure it still works (2 reports say
  no; application package not found).
- Make ``current_route_url`` a method of request.
- Add narrative docs for wsgiapp and wsgiapp2.
- Create a ``current_route_path`` function and make it a method of request.
- "static_path" API (omit host and port).
- Provide a way to set the authentication policy and the authorization policy
  during a config.include (they are related, so just exposing the currently
  underscored-private _set_auth* methods won't cut it).
- Try to figure out a way to keep "settings" as the original dictionary
  passed to the Configurator instead of copying it.
- Merge aodag's config.include(route_prefix=...) fork.
- Merge Michael's route group work.
- Kill off ``bfg.routes`` envvars in router.
- Alias the stupid long default session factory name.
- Fix indirect circular import between router and config.
- Eliminate non-deployment-non-scaffold-related Paste dependencies:
  ``paste.urlparser.StaticURLParser``, ``paste.auth.auth_tkt`` (cutnpaste or
  reimplement both).
Nice-to-Have
------------
- Maybe add ``add_renderer_globals`` method to Configurator.
- Add narrative docs for wsgiapp and wsgiapp2.
- Provide a ``has_view`` function.
- Debug option to print view matching decision (e.g. debug_viewlookup or so).
- Speed up startup time (defer _bootstrap and registerCommonDirectives()
  until needed by ZCML, as well as unfound speedups).
- Nicer Mako exceptions in WebError.
- Response.RequestClass should probably be pyramid.request.Request but this
  may imply actually subclassing webob.Response
- Nicer Mako exceptions in debug toolbar.
- Better "Extending" chapter.
@@ -62,9 +91,10 @@
- Create a function which performs a recursive request.
- Debug option to print view matching decision.
- Update App engine chapter with less creaky directions.
Probably Bad Ideas
------------------
- Add functionality that mocks the behavior of ``repoze.browserid``.
@@ -72,5 +102,5 @@
  http://plope.com/pyramid_auth_design_api_postmortem, phasing out the
  current auth-n-auth abstractions in a backwards compatible way.
- Add doc string for BeforeRender event with more details.
- Maybe add ``add_renderer_globals`` method to Configurator.
docs/api.rst
@@ -28,9 +28,11 @@
   api/security
   api/session
   api/settings
   api/static
   api/testing
   api/threadlocal
   api/traversal
   api/tweens
   api/url
   api/view
   api/wsgi
docs/api/config.rst
@@ -44,7 +44,7 @@
     .. automethod:: add_route
     .. automethod:: add_static_view(name, path, cache_max_age=3600, permission='__no_permission_required__')
     .. automethod:: add_static_view(name, path, cache_max_age=3600, permission=NO_PERMISSION_REQUIRED)
     .. automethod:: add_settings
@@ -78,6 +78,8 @@
     .. automethod:: set_view_mapper
     .. automethod:: add_tween
     .. automethod:: testing_securitypolicy
     .. automethod:: testing_resources
@@ -86,3 +88,15 @@
     .. automethod:: testing_add_renderer
  .. attribute:: global_registries
     The set of registries that have been created for :app:`Pyramid`
     applications, one per each call to
     :meth:`pyramid.config.Configurator.make_wsgi_app` in the current
     process. The object itself supports iteration and has a ``last``
     property containing the last registry loaded.
     The registries contained in this object are stored as weakrefs,
     thus they will only exist for the lifetime of the actual
     applications for which they are being used.
docs/api/interfaces.rst
@@ -9,14 +9,19 @@
++++++++++++++++++++++++
  .. autointerface:: IApplicationCreated
     :members:
  .. autointerface:: INewRequest
     :members:
  .. autointerface:: IContextFound
     :members:
  .. autointerface:: INewResponse
     :members:
  .. autointerface:: IBeforeRender
     :members:
Other Interfaces
++++++++++++++++
docs/api/paster.rst
@@ -3,11 +3,15 @@
:mod:`pyramid.paster`
---------------------------
.. module:: pyramid.paster
.. automodule:: pyramid.paster
.. function:: get_app(config_file, name)
    .. function:: get_app(config_uri, name=None)
    Return the WSGI application named ``name`` in the PasteDeploy
    config file ``config_file``.
        Return the WSGI application named ``name`` in the PasteDeploy
        config file specified by ``config_uri``.
        If the ``name`` is None, this will attempt to parse the name from
        the ``config_uri`` string expecting the format ``inifile#name``.
        If no name is found, the name will default to "main".
    .. autofunction:: bootstrap
docs/api/renderers.rst
@@ -13,3 +13,12 @@
.. autoclass:: JSONP
.. attribute:: null_renderer
   An object that can be used in advanced integration cases as input to the
   view configuration ``renderer=`` argument.  When the null renderer is used
   as a view renderer argument, Pyramid avoids converting the view callable
   result into a Response object.  This is useful if you want to reuse the
   view configuration and lookup machinery outside the context of its use by
   the Pyramid router (e.g. the package named ``pyramid_rpc`` does this).
docs/api/request.rst
@@ -85,6 +85,17 @@
     of ``request.exception`` will be ``None`` within response and
     finished callbacks.
   .. attribute:: exc_info
     If an exception was raised by a :term:`root factory` or a :term:`view
     callable`, or at various other points where :app:`Pyramid` executes
     user-defined code during the processing of a request, result of
     ``sys.exc_info()`` will be available as the ``exc_info`` attribute of
     the request within a :term:`exception view`, a :term:`response callback`
     or a :term:`finished callback`.  If no exception occurred, the value of
     ``request.exc_info`` will be ``None`` within response and finished
     callbacks.
   .. attribute:: response
     This attribute is actually a "reified" property which returns an
@@ -180,12 +191,12 @@
      object (exposed to view code as ``request.response``) to influence
      rendered response behavior.
   .. attribute:: json
   .. attribute:: json_body
      If the request's ``content_type`` is ``application/json``, this
      attribute will contain the JSON-decoded variant of the request body.
      If the request's ``content_type`` is not ``application/json``, this
      attribute will be ``None``.
       This property will return the JSON-decoded variant of the request
       body.  If the request body is not well-formed JSON, or there is no
       body associated with this request, this property will raise an
       exception.  See also :ref:`request_json_body`.
.. note::
docs/api/response.rst
@@ -9,3 +9,8 @@
   :members:
   :inherited-members:
Functions
~~~~~~~~~
.. autofunction:: response_adapter
docs/api/scripting.rst
@@ -7,3 +7,5 @@
  .. autofunction:: get_root
  .. autofunction:: prepare
docs/api/security.rst
@@ -57,6 +57,8 @@
    last ACE in an ACL in systems that use an "inheriting" security
    policy, representing the concept "don't inherit any other ACEs".
.. attribute:: NO_PERMISSION_REQUIRED
Return Values
-------------
docs/api/static.rst
New file
@@ -0,0 +1,11 @@
.. _static_module:
:mod:`pyramid.static`
---------------------
.. automodule:: pyramid.static
  .. autoclass:: static_view
     :members:
     :inherited-members:
docs/api/tweens.rst
New file
@@ -0,0 +1,25 @@
.. _tweens_module:
:mod:`pyramid.tweens`
---------------------
.. automodule:: pyramid.tweens
   .. autofunction:: excview_tween_factory
   .. attribute:: MAIN
      Constant representing the main Pyramid handling function, for use in
      ``under`` and ``over`` arguments to
      :meth:`pyramid.config.Configurator.add_tween`.
   .. attribute:: INGRESS
      Constant representing the request ingress, for use in ``under`` and
      ``over`` arguments to :meth:`pyramid.config.Configurator.add_tween`.
   .. attribute:: EXCVIEW
      Constant representing the exception view tween, for use in ``under``
      and ``over`` arguments to
      :meth:`pyramid.config.Configurator.add_tween`.
docs/authorintro.rst
@@ -165,6 +165,7 @@
   single: Shipman, John
   single: Beelby, Chris
   single: Paez, Patricio
   single: Merickel, Michael
Thanks
======
@@ -179,8 +180,9 @@
Sawyers, Malthe Borch, Carlos de la Guardia, Chris Rossi, Shane Hathaway,
Daniel Holth, Wichert Akkerman, Georg Brandl, Blaise Laflamme, Ben Bangert,
Casey Duncan, Hugues Laflamme, Mike Orr, John Shipman, Chris Beelby, Patricio
Paez, Simon Oram, Nat Hardwick, Ian Bicking, Jim Fulton, Tom Moroz of the
Open Society Institute, and Todd Koym of Environmental Health Sciences.
Paez, Simon Oram, Nat Hardwick, Ian Bicking, Jim Fulton, Michael Merickel,
Tom Moroz of the Open Society Institute, and Todd Koym of Environmental
Health Sciences.
Thanks to Guido van Rossum and Tim Peters for Python.
docs/conf.py
@@ -93,7 +93,7 @@
# other places throughout the built documents.
#
# The short X.Y version.
version = '1.1a4'
version = '1.1'
# The full version, including alpha/beta/rc tags.
release = version
docs/copyright.rst
@@ -1,7 +1,7 @@
Copyright, Trademarks, and Attributions
=======================================
*The Pyramid Web Application Development Framework, Version 1.0*
*The Pyramid Web Application Development Framework, Version 1.1*
by Chris McDonough
@@ -55,7 +55,11 @@
  Ben Bangert, Blaise Laflamme, Rob Miller, Mike Orr, Carlos de la Guardia,
  Paul Everitt, Tres Seaver, John Shipman, Marius Gedminas, Chris Rossi,
  Joachim Krebs, Xavier Spriet, Reed O'Brien, William Chambers, Charlie
  Choiniere, Jamaludin Ahmad, Graham Higgins, Patricio Paez.
  Choiniere, Jamaludin Ahmad, Graham Higgins, Patricio Paez, Michael
  Merickel, Eric Ongerth, Niall O'Higgins, Christoph Zwerschke, John
  Anderson, Atsushi Odagiri, Kirk Strauser, JD Navarro, Joe Dallago,
  Savoir-Faire Linux, Łukasz Fidosz, Christopher Lambacher, Claus Conrad,
  Chris Beelby, and a number of people with only psuedonyms on GitHub.
Cover Designer:
   Hugues Laflamme of `Kemeneur <http://www.kemeneur.com/>`_.
docs/designdefense.rst
@@ -711,9 +711,9 @@
This is true.  At the time of this writing, the total number of Python
package distributions that :app:`Pyramid` depends upon transitively is 18 if
you use Python 2.6 or 2.7, or 16 if you use Python 2.4 or 2.5.  This is a lot
more than zero package distribution dependencies: a metric which various
Python microframeworks and Django boast.
you use Python 2.6 or 2.7, or 16 if you use Python 2.5.  This is a lot more
than zero package distribution dependencies: a metric which various Python
microframeworks and Django boast.
The :mod:`zope.component` and :mod:`zope.configuration` packages on which
:app:`Pyramid` depends have transitive dependencies on several other packages
@@ -1125,10 +1125,11 @@
<http://bobo.digicool.com/>`_ doesn't describe itself as a microframework,
but its intended userbase is much the same.  Many others exist.  We've
actually even (only as a teaching tool, not as any sort of official project)
`created one using BFG <http://bfg.repoze.org/videos#groundhog1>`_ (the
precursor to Pyramid). 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.
`created one using Pyramid <http://bfg.repoze.org/videos#groundhog1>`_ (the
videos use BFG, a precursor to Pyramid, but the resulting code is `available
for Pyramid too <http://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
@@ -1394,8 +1395,8 @@
  a registry in another module.  This has the effect that
  double-registrations will never be performed.
Routes (Usually) Need Relative Ordering
+++++++++++++++++++++++++++++++++++++++
Routes Need Relative Ordering
+++++++++++++++++++++++++++++
Consider the following simple `Groundhog
<https://github.com/Pylons/groundhog>`_ application:
@@ -1470,19 +1471,51 @@
matches first.  A 404 error is raised.  This is not what we wanted; it just
happened due to the order in which we defined our view functions.
You may be willing to maintain an ordering of your view functions which
reifies your routing policy.  Your application may be small enough where this
will never cause an issue.  If it becomes large enough to matter, however, I
don't envy you.  Maintaining that ordering as your application grows larger
will be difficult.  At some point, you will also need to start controlling
*import* ordering as well as function definition ordering.  When your
application grows beyond the size of a single file, and when decorators are
used to register views, the non-``__main__`` modules which contain
configuration decorators must be imported somehow for their configuration to
be executed.
This is because "Groundhog" routes are added to the routing map in import
order, and matched in the same order when a request comes in.  Bottle, like
Groundhog, as of this writing, matches routes in the order in which they're
defined at Python execution time.  Flask, on the other hand, does not order
route matching based on import order; it reorders the routes you add to your
application based on their "complexity".  Other microframeworks have varying
strategies to do route ordering.
Does that make you a little uncomfortable?  It should, because
Your application may be small enough where route ordering will never cause an
issue.  If your application becomes large enough, however, being able to
specify or predict that ordering as your application grows larger will be
difficult.  At some point, you will likely need to more explicitly start
controlling route ordering, especially in applications that require
extensibility.
If your microframework orders route matching based on "complexity", you'll
need to understand what that "complexity" ordering is and attempt to inject a
"less complex" route to have it get matched before any "more complex" one to
ensure that it's tried first.
If your microframework orders its route matching based on relative
import/execution of function decorator definitions, you will need to ensure
you execute all of these statements in the "right" order, and you'll need to
be cognizant of this import/execution ordering as you grow your application
or try to extend it.  This is a difficult invariant to maintain for all but
the smallest applications.
In either case, your application must import the non-``__main__`` modules
which contain configuration decorations somehow for their configuration to be
executed.  Does that make you a little uncomfortable?  It should, because
:ref:`you_dont_own_modulescope`.
Pyramid uses neither decorator import time ordering nor does it attempt to
divine the relative "complexity" of one route to another in order to define a
route match ordering.  In Pyramid, you have to maintain relative route
ordering imperatively via the chronology of multiple executions of the
:meth:`pyramid.config.Configurator.add_route` method.  The order in which you
repeatedly call ``add_route`` becomes the order of route matching.
If needing to maintain this imperative ordering truly bugs you, you can use
:term:`traversal` instead of route matching, which is a completely
declarative (and completely predictable) mechanism to map code to URLs.
While URL dispatch is easier to understand for small non-extensible
applications, traversal is a great fit for very large applications and
applications that need to be arbitrarily extensible.
"Stacked Object Proxies" Are Too Clever / Thread Locals Are A Nuisance
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -1728,7 +1761,7 @@
Pyramid has ~ 650 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
#repoze and/or #pylons IRC channels on freenode.net and see.
#pyramid and/or #pylons IRC channels on freenode.net and see.
Hate Zope
+++++++++
docs/glossary.rst
@@ -23,6 +23,11 @@
     a subclass such as :class:`pyramid.httpexceptions.HTTPFound`.  See
     :ref:`webob_chapter` for information about response objects.
   response adapter
     A callable which accepts an arbitrary object and "converts" it to a
     :class:`pyramid.response.Response` object.  See :ref:`using_iresponse`
     for more information.
   Repoze
     "Repoze" is essentially a "brand" of software developed by `Agendaless
     Consulting <http://agendaless.com>`_ and a set of contributors.  The
@@ -326,7 +331,7 @@
      :term:`ZODB` database.
   WebOb
     `WebOb <http://pythonpaste.org/webob/>`_ is a WSGI request/response
     `WebOb <http://webob.org>`_ is a WSGI request/response
     library created by Ian Bicking.
   Paste
@@ -506,6 +511,9 @@
     `JavaScript Object Notation <http://www.json.org/>`_ is a data
     serialization format.
   jQuery
     A popular `Javascript library <http://jquery.org>`_.
   renderer
     A serializer that can be referred to via :term:`view
     configuration` which converts a non-:term:`Response` return
@@ -589,8 +597,9 @@
     declaration` required by your application.
   declarative configuration
     The configuration mode in which you use :term:`ZCML` to make a set of
     :term:`configuration declaration` statements.  See :term:`pyramid_zcml`.
     The configuration mode in which you use the combination of
     :term:`configuration decoration` and a :term:`scan` to configure your
     Pyramid application.
   Not Found view
      An :term:`exception view` invoked by :app:`Pyramid` when the
@@ -904,3 +913,20 @@
     A :term:`response` that is generated as the result of a raised exception
     being caught by an :term:`exception view`.
   PyPy
     PyPy is an "alternative implementation of the Python
     language":http://pypy.org/
   tween
     A bit of code that sits between the Pyramid router's main request
     handling function and the upstream WSGI component that uses
     :app:`Pyramid` as its 'app'.  The word "tween" is a contraction of
     "between".  A tween may be used by Pyramid framework extensions, to
     provide, for example, Pyramid-specific view timing support, bookkeeping
     code that examines exceptions before they are returned to the upstream
     WSGI application, or a variety of other features.  Tweens behave a bit
     like :mod:`WSGI` 'middleware' but they have the benefit of running in a
     context in which they have access to the Pyramid :term:`application
     registry` as well as the Pyramid rendering machinery.  See
     :ref:`registering_tweens`.
docs/index.rst
@@ -24,8 +24,8 @@
.. toctree::
   :maxdepth: 1
   whatsnew-1.0
   whatsnew-1.1
   whatsnew-1.0
Narrative documentation
=======================
@@ -43,23 +43,24 @@
   narr/project
   narr/startup
   narr/urldispatch
   narr/muchadoabouttraversal
   narr/traversal
   narr/views
   narr/renderers
   narr/templates
   narr/viewconfig
   narr/resources
   narr/assets
   narr/webob
   narr/sessions
   narr/security
   narr/hybrid
   narr/i18n
   narr/vhosting
   narr/events
   narr/environment
   narr/commandline
   narr/i18n
   narr/vhosting
   narr/testing
   narr/resources
   narr/muchadoabouttraversal
   narr/traversal
   narr/security
   narr/hybrid
   narr/hooks
   narr/advconfig
   narr/extending
docs/latexindex.rst
@@ -32,23 +32,24 @@
   narr/firstapp
   narr/project
   narr/urldispatch
   narr/muchadoabouttraversal
   narr/traversal
   narr/views
   narr/renderers
   narr/templates
   narr/viewconfig
   narr/resources
   narr/assets
   narr/webob
   narr/sessions
   narr/security
   narr/hybrid
   narr/i18n
   narr/vhosting
   narr/events
   narr/environment
   narr/commandline
   narr/i18n
   narr/vhosting
   narr/testing
   narr/resources
   narr/muchadoabouttraversal
   narr/traversal
   narr/security
   narr/hybrid
   narr/hooks
   narr/advconfig
   narr/extending
docs/make_book
@@ -1,4 +1,4 @@
#!/bin/sh
make clean latex SPHINXBUILD=../bookenv/bin/sphinx-build BOOK=1
make clean latex SPHINXBUILD=../env26/bin/sphinx-build BOOK=1
cd _build/latex && make all-pdf
docs/make_epub
New file
@@ -0,0 +1,2 @@
#!/bin/sh
make clean epub SPHINXBUILD=../env26/bin/sphinx-build
docs/make_pdf
New file
@@ -0,0 +1,4 @@
#!/bin/sh
make clean latex SPHINXBUILD=../env26/bin/sphinx-build
cd _build/latex && make all-pdf
docs/narr/MyProject/development.ini
@@ -1,15 +1,16 @@
[app:MyProject]
use = egg:MyProject
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
pyramid.include = pyramid_debugtoolbar
[pipeline:main]
pipeline =
    egg:WebError#evalerror
    MyProject
[server:main]
docs/narr/MyProject/myproject/templates/mytemplate.pt
@@ -46,7 +46,7 @@
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get"
                action="http://docs.pylonsproject.org/pyramid/dev/search.html">
                action="http://docs.pylonsproject.org/pyramid/current/search.html">
            <input type="text" id="q" name="q" value="" />
            <input type="submit" id="x" value="Go" />
          </form>
@@ -60,32 +60,32 @@
              </a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">
                Narrative Documentation
              </a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">
                API Documentation
              </a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">
                Tutorials
              </a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">
                Change History
              </a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">
                Sample Applications
              </a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">
                Support and Development
              </a>
            </li>
docs/narr/MyProject/production.ini
@@ -1,11 +1,12 @@
[app:MyProject]
use = egg:MyProject
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
[filter:weberror]
use = egg:WebError#error_catcher
docs/narr/MyProject/setup.py
@@ -6,7 +6,7 @@
README = open(os.path.join(here, 'README.txt')).read()
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = ['pyramid', 'WebError']
requires = ['pyramid', 'pyramid_debugtoolbar', 'WebError']
setup(name='MyProject',
      version='0.0',
docs/narr/advconfig.rst
@@ -14,7 +14,7 @@
circumstances.
.. index::
   single: imperative configuration
   pair: configuration; conflict detection
.. _conflict_detection:
@@ -299,6 +299,9 @@
provides conflict detection, because it's implemented in terms of the
conflict-aware ``add_route`` and ``add_view`` methods.
.. index::
   pair: configuration; including from external sources
.. _including_configuration:
Including Configuration from External Sources
@@ -397,6 +400,10 @@
constraints are not absolved by two-phase configuration.  Routes are still
added in configuration execution order.
.. index::
   single: add_directive
   pair: configurator; adding directives
.. _add_directive:
Adding Methods to the Configurator via ``add_directive``
docs/narr/assets.rst
@@ -35,6 +35,9 @@
``.css``, ``.js``, and ``.gif`` files.  These asset files are delivered when
a user visits an application URL.
.. index::
   single: asset specifications
.. _asset_specifications:
Understanding Asset Specifications
@@ -85,6 +88,7 @@
.. index::
   single: add_static_view
   pair: assets; serving
.. _static_assets_section:
@@ -186,6 +190,7 @@
.. index::
   single: generating static asset urls
   single: static asset urls
   pair:   assets; generating urls
.. _generating_static_asset_urls:
@@ -299,7 +304,7 @@
Root-Relative Custom Static View (URL Dispatch Only)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The :class:`pyramid.view.static` helper class generates a Pyramid view
The :class:`pyramid.static.static_view` helper class generates a Pyramid view
callable.  This view callable can serve static assets from a directory.  An
instance of this class is actually used by the
:meth:`~pyramid.config.Configurator.add_static_view` configuration method, so
@@ -310,26 +315,27 @@
   exclusively.  The root-relative route we'll be registering will always be
   matched before traversal takes place, subverting any views registered via
   ``add_view`` (at least those without a ``route_name``).  A
   :class:`~pyramid.view.static` static view cannot be made root-relative when
   you use traversal.
   :class:`~pyramid.static.static_view` static view cannot be made
   root-relative when you use traversal unless it's registered as a
   :term:`Not Found view`.
To serve files within a directory located on your filesystem at
``/path/to/static/dir`` as the result of a "catchall" route hanging from the
root that exists at the end of your routing table, create an instance of the
:class:`~pyramid.view.static` class inside a ``static.py`` file in your
application root as below.
:class:`~pyramid.static.static_view` class inside a ``static.py`` file in
your application root as below.
.. ignore-next-block
.. code-block:: python
   :linenos:
   from pyramid.view import static
   static_view = static('/path/to/static/dir')
   from pyramid.static import static
   static_view = static_view('/path/to/static/dir', use_subpath=True)
.. note:: For better cross-system flexibility, use an :term:`asset
   specification` as the argument to :class:`~pyramid.view.static` instead of
   a physical absolute filesystem path, e.g. ``mypackage:static`` instead of
   ``/path/to/mypackage/static``.
   specification` as the argument to :class:`~pyramid.static.static_view`
   instead of a physical absolute filesystem path, e.g. ``mypackage:static``
   instead of ``/path/to/mypackage/static``.
Subsequently, you may wire the files that are served by this view up to be
accessible as ``/<filename>`` using a configuration method in your
@@ -345,8 +351,8 @@
   config.add_view('myapp.static.static_view', route_name='catchall_static')
The special name ``*subpath`` above is used by the
:class:`~pyramid.view.static` view callable to signify the path of the file
relative to the directory you're serving.
:class:`~pyramid.static.static_view` view callable to signify the path of the
file relative to the directory you're serving.
Registering A View Callable to Serve a "Static" Asset
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -425,10 +431,10 @@
- A directory containing multiple Chameleon templates.
- Individual static files served up by an instance of the
  ``pyramid.view.static`` helper class.
  ``pyramid.static.static_view`` helper class.
- A directory of static files served up by an instance of the
  ``pyramid.view.static`` helper class.
  ``pyramid.static.static_view`` helper class.
- Any other asset (or set of assets) addressed by code that uses the
  setuptools :term:`pkg_resources` API.
docs/narr/commandline.rst
New file
@@ -0,0 +1,560 @@
.. _command_line_chapter:
Command-Line Pyramid
====================
Your :app:`Pyramid` application can be controlled and inspected using a
variety of command-line utilities.  These utilities are documented in this
chapter.
.. index::
   pair: matching views; printing
   single: paster pviews
.. _displaying_matching_views:
Displaying Matching Views for a Given URL
-----------------------------------------
For a big application with several views, it can be hard to keep the view
configuration details in your head, even if you defined all the views
yourself. You can use the ``paster pviews`` command in a terminal window to
print a summary of matching routes and views for a given URL in your
application. The ``paster pviews`` command accepts two arguments. The first
argument to ``pviews`` is the path to your application's ``.ini`` file and
section name inside the ``.ini`` file which points to your application.  This
should be of the format ``config_file#section_name``. The second argument is
the URL to test for matching views.  The ``section_name`` may be omitted; if
it is, it's considered to be ``main``.
Here is an example for a simple view configuration using :term:`traversal`:
.. code-block:: text
   :linenos:
   $ ../bin/paster pviews development.ini#tutorial /FrontPage
   URL = /FrontPage
       context: <tutorial.models.Page object at 0xa12536c>
       view name:
       View:
       -----
       tutorial.views.view_page
       required permission = view
The output always has the requested URL at the top and below that all the
views that matched with their view configuration details. In this example
only one view matches, so there is just a single *View* section. For each
matching view, the full code path to the associated view callable is shown,
along with any permissions and predicates that are part of that view
configuration.
A more complex configuration might generate something like this:
.. code-block:: text
   :linenos:
   $ ../bin/paster pviews development.ini#shootout /about
   URL = /about
       context: <shootout.models.RootFactory object at 0xa56668c>
       view name: about
       Route:
       ------
       route name: about
       route pattern: /about
       route path: /about
       subpath:
       route predicates (request method = GET)
           View:
           -----
           shootout.views.about_view
           required permission = view
           view predicates (request_param testing, header X/header)
       Route:
       ------
       route name: about_post
       route pattern: /about
       route path: /about
       subpath:
       route predicates (request method = POST)
           View:
           -----
           shootout.views.about_view_post
           required permission = view
           view predicates (request_param test)
           View:
           -----
           shootout.views.about_view_post2
           required permission = view
           view predicates (request_param test2)
In this case, we are dealing with a :term:`URL dispatch` application. This
specific URL has two matching routes. The matching route information is
displayed first, followed by any views that are associated with that route.
As you can see from the second matching route output, a route can be
associated with more than one view.
For a URL that doesn't match any views, ``paster pviews`` will simply print
out a *Not found* message.
.. index::
   single: interactive shell
   single: IPython
   single: paster pshell
   single: pshell
.. _interactive_shell:
The Interactive Shell
---------------------
Once you've installed your program for development using ``setup.py
develop``, you can use an interactive Python shell to execute expressions in
a Python environment exactly like the one that will be used when your
application runs "for real".  To do so, use the ``paster pshell`` command.
The argument to ``pshell`` follows the format ``config_file#section_name``
where ``config_file`` is the path to your application's ``.ini`` file and
``section_name`` is the ``app`` section name inside the ``.ini`` file which
points to your application.  For example, if your application ``.ini`` file
might have a ``[app:MyProject]`` section that looks like so:
.. code-block:: ini
   :linenos:
   [app:MyProject]
   use = egg:MyProject
   pyramid.reload_templates = true
   pyramid.debug_authorization = false
   pyramid.debug_notfound = false
   pyramid.debug_templates = true
   pyramid.default_locale_name = en
If so, you can use the following command to invoke a debug shell using the
name ``MyProject`` as a section name:
.. code-block:: text
    chrism@thinko env26]$ bin/paster pshell starter/development.ini#MyProject
    Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32)
    [GCC 4.4.3] on linux2
    Type "help" for more information.
    Environment:
      app          The WSGI application.
      registry     Active Pyramid registry.
      request      Active request object.
      root         Root of the default resource tree.
      root_factory Default root factory used to create `root`.
    >>> root
    <myproject.resources.MyResource object at 0x445270>
    >>> registry
    <Registry myproject>
    >>> registry.settings['pyramid.debug_notfound']
    False
    >>> from myproject.views import my_view
    >>> from pyramid.request import Request
    >>> r = Request.blank('/')
    >>> my_view(r)
    {'project': 'myproject'}
The WSGI application that is loaded will be available in the shell as the
``app`` global. Also, if the application that is loaded is the :app:`Pyramid`
app with no surrounding middleware, the ``root`` object returned by the
default :term:`root factory`, ``registry``, and ``request`` will be
available.
You can also simply rely on the ``main`` default section name by omitting any
hash after the filename:
.. code-block:: text
    chrism@thinko env26]$ bin/paster pshell starter/development.ini
Press ``Ctrl-D`` to exit the interactive shell (or ``Ctrl-Z`` on Windows).
.. index::
   pair: pshell; extending
.. _extending_pshell:
Extending the Shell
~~~~~~~~~~~~~~~~~~~
It is sometimes convenient when using the interactive shell often to have
some variables significant to your application already loaded as globals when
you start the ``pshell``. To facilitate this, ``pshell`` will look for a
special ``[pshell]`` section in your INI file and expose the subsequent
key/value pairs to the shell.  Each key is a variable name that will be
global within the pshell session; each value is a :term:`dotted Python name`.
For example, you want to expose your model to the shell, along with the
database session so that you can mutate the model on an actual database.
Here, we'll assume your model is stored in the ``myapp.models`` package.
.. code-block:: ini
   :linenos:
   [pshell]
   m = myapp.models
   session = myapp.models.DBSession
   t = transaction
When this INI file is loaded, the extra variables ``m``, ``session`` and
``t`` will be available for use immediately. For example:
.. code-block:: text
    chrism@thinko env26]$ bin/paster pshell starter/development.ini
    Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32)
    [GCC 4.4.3] on linux2
    Type "help" for more information.
    Environment:
      app          The WSGI application.
      registry     Active Pyramid registry.
      request      Active request object.
      root         Root of the default resource tree.
      root_factory Default root factory used to create `root`.
    Custom Variables:
      m            myapp.models
      session      myapp.models.DBSession
      t            transaction
    >>>
.. index::
   single: IPython
IPython
~~~~~~~
If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ installed in
the interpreter you use to invoke the ``paster`` command, the ``pshell``
command will use an IPython interactive shell instead of a standard Python
interpreter shell.  If you don't want this to happen, even if you have
IPython installed, you can pass the ``--disable-ipython`` flag to the
``pshell`` command to use a standard Python interpreter shell
unconditionally.
.. code-block:: text
   [chrism@vitaminf shellenv]$ ../bin/paster pshell --disable-ipython \
                                development.ini#MyProject
.. index::
   pair: routes; printing
   single: paster proutes
   single: proutes
.. _displaying_application_routes:
Displaying All Application Routes
---------------------------------
You can use the ``paster proutes`` command in a terminal window to print a
summary of routes related to your application.  Much like the ``paster
pshell`` command (see :ref:`interactive_shell`), the ``paster proutes``
command accepts one argument with the format ``config_file#section_name``.
The ``config_file`` is the path to your application's ``.ini`` file, and
``section_name`` is the ``app`` section name inside the ``.ini`` file which
points to your application.  By default, the ``section_name`` is ``main`` and
can be omitted.
For example:
.. code-block:: text
   :linenos:
   [chrism@thinko MyProject]$ ../bin/paster proutes development.ini#MyProject
   Name            Pattern                        View
   ----            -------                        ----
   home            /                              <function my_view>
   home2           /                              <function my_view>
   another         /another                       None
   static/         static/*subpath                <static_view object>
   catchall        /*subpath                      <function static_view>
``paster proutes`` generates a table.  The table has three columns: a Name
column, a Pattern column, and a View column.  The items listed in the
Name column are route names, the items listed in the Pattern column are route
patterns, and the items listed in the View column are representations of the
view callable that will be invoked when a request matches the associated
route pattern.  The view column may show ``None`` if no associated view
callable could be found.  If no routes are configured within your
application, nothing will be printed to the console when ``paster proutes``
is executed.
.. index::
   pair: tweens; printing
   single: paster ptweens
   single: ptweens
.. _displaying_tweens:
Displaying "Tweens"
-------------------
A :term:`tween` is a bit of code that sits between the main Pyramid
application request handler and the WSGI application which calls it.  A user
can get a representation of both the implicit tween ordering (the ordering
specified by calls to :meth:`pyramid.config.Configurator.add_tween`) and the
explicit tween ordering (specified by the ``pyramid.tweens`` configuration
setting) orderings using the ``paster ptweens`` command.  Handler factories
which are functions or classes will show up as a standard Python dotted name
in the ``paster ptweens`` output.  Tween factories which are *instances* will
show their module and class name; the Python object id of the instance will
be appended.
For example, here's the ``paster pwteens`` command run against a system
configured without any explicit tweens:
.. code-block:: text
   :linenos:
   [chrism@thinko pyramid]$ paster ptweens development.ini
   "pyramid.tweens" config value NOT set (implicitly ordered tweens used)
   Implicit Tween Chain
   Position    Name                                                Alias
   --------    ----                                                -----
   -           -                                                   INGRESS
   0           pyramid_debugtoolbar.toolbar.toolbar_tween_factory  pdbt
   1           pyramid.tweens.excview_tween_factory                excview
   -           -                                                   MAIN
Here's the ``paster pwteens`` command run against a system configured *with*
explicit tweens defined in its ``development.ini`` file:
.. code-block:: text
   :linenos:
   [chrism@thinko pyramid]$ paster ptweens development.ini
   "pyramid.tweens" config value set (explicitly ordered tweens used)
   Explicit Tween Chain (used)
   Position    Name
   --------    ----
   -           INGRESS
   0           starter.tween_factory2
   1           starter.tween_factory1
   2           pyramid.tweens.excview_tween_factory
   -           MAIN
   Implicit Tween Chain (not used)
   Position    Name                                                Alias
   --------    ----                                                -----
   -           -                                                   INGRESS
   0           pyramid_debugtoolbar.toolbar.toolbar_tween_factory  pdbt
   1           pyramid.tweens.excview_tween_factory                excview
   -           -                                                   MAIN
Here's the application configuration section of the ``development.ini`` used
by the above ``paster ptweens`` command which reprorts that the explicit
tween chain is used:
.. code-block:: text
   :linenos:
   [app:starter]
   use = egg:starter
   reload_templates = true
   debug_authorization = false
   debug_notfound = false
   debug_routematch = false
   debug_templates = true
   default_locale_name = en
   pyramid.include = pyramid_debugtoolbar
   pyramid.tweens = starter.tween_factory2
                    starter.tween_factory1
                    pyramid.tweens.excview_tween_factory
See :ref:`registering_tweens` for more information about tweens.
.. _writing_a_script:
Writing a Script
----------------
All web applications are, at their hearts, systems which accept a request and
return a response.  When a request is accepted by a :app:`Pyramid`
application, the system receives state from the request which is later relied
on by your application code.  For example, one :term:`view callable` may assume
it's working against a request that has a ``request.matchdict`` of a
particular composition, while another assumes a different composition of the
matchdict.
In the meantime, it's convenient to be able to write a Python script that can
work "in a Pyramid environment", for instance to update database tables used
by your :app:`Pyramid` application.  But a "real" Pyramid environment doesn't
have a completely static state independent of a request; your application
(and Pyramid itself) is almost always reliant on being able to obtain
information from a request.  When you run a Python script that simply imports
code from your application and tries to run it, there just is no request
data, because there isn't any real web request.  Therefore some parts of your
application and some Pyramid APIs will not work.
For this reason, :app:`Pyramid` makes it possible to run a script in an
environment much like the environment produced when a particular
:term:`request` reaches your :app:`Pyramid` application.  This is achieved by
using the :func:`pyramid.paster.bootstrap` command in the body of your
script.
.. note:: This feature is new as of :app:`Pyramid` 1.1.
In the simplest case, :func:`pyramid.paster.bootstrap` can be used with a
single argument, which accepts the :term:`PasteDeploy` ``.ini`` file
representing Pyramid your application configuration as a single argument:
.. code-block:: python
   from pyramid.paster import bootstrap
   env = bootstrap('/path/to/my/development.ini')
   print env['request'].route_url('home')
:func:`pyramid.paster.bootstrap` returns a dictionary containing
framework-related information.  This dictionary will always contain a
:term:`request` object as its ``request`` key.
The following keys are available in the ``env`` dictionary returned by
:func:`pyramid.paster.bootstrap`:
request
    A :class:`pyramid.request.Request` object implying the current request
    state for your script.
app
    The :term:`WSGI` application object generated by bootstrapping.
root
    The :term:`resource` root of your :app:`Pyramid` application.  This is an
    object generated by the :term:`root factory` configured in your
    application.
registry
    The :term:`application registry` of your :app:`Pyramid` application.
closer
    A parameterless callable that can be used to pop an internal
    :app:`Pyramid` threadlocal stack (used by
    :func:`pyramid.threadlocal.get_current_registry` and
    :func:`pyramid.threadlocal.get_current_request`) when your scripting job
    is finished.
Let's assume that the ``/path/to/my/development.ini`` file used in the
example above looks like so:
.. code-block:: ini
   [pipeline:main]
   pipeline = egg:WebError#evalerror
              another
   [app:another]
   use = egg:MyProject
The configuration loaded by the above bootstrap example will use the
configuration implied by the ``[pipeline:main]`` section of your
configuration file by default.  Specifying ``/path/to/my/development.ini`` is
logically equivalent to specifying ``/path/to/my/development.ini#main``.  In
this case, we'll be using a configuration that includes an ``app`` object
which is wrapped in the WebError ``evalerror`` middleware.
You can also specify a particular *section* of the PasteDeploy ``.ini`` file
to load instead of ``main``:
.. code-block:: python
   from pyramid.paster import bootstrap
   env = bootstrap('/path/to/my/development.ini#another')
   print env['request'].route_url('home')
The above example specifies the ``another`` ``app``, ``pipeline``, or
``composite`` section of your PasteDeploy configuration file. The ``app``
object present in the ``env`` dictionary returned by
:func:`pyramid.paster.bootstrap` will be a :app:`Pyramid` :term:`router`.
Changing the Request
~~~~~~~~~~~~~~~~~~~~
By default, Pyramid will generate a request object in the ``env`` dictionary
for the URL ``http://localhost:80/``. This means that any URLs generated
by Pyramid during the execution of your script will be anchored here. This
is generally not what you want.
So how do we make Pyramid generate the correct URLs?
Assuming that you have a route configured in your application like so:
.. code-block:: python
   config.add_route('verify', '/verify/{code}')
You need to inform the Pyramid environment that the WSGI application is
handling requests from a certain base. For example, we want to mount our
application at `example.com/prefix` and the generated URLs should use HTTPS.
This can be done by mutating the request object:
.. code-block:: python
   from pyramid.paster import bootstrap
   env = bootstrap('/path/to/my/development.ini#another')
   env['request'].host = 'example.com'
   env['request'].scheme = 'https'
   env['request'].script_name = '/prefix'
   print env['request'].application_url
   # will print 'https://example.com/prefix/another/url'
Now you can readily use Pyramid's APIs for generating URLs:
.. code-block:: python
   route_url('verify', env['request'], code='1337')
   # will return 'https://example.com/prefix/verify/1337'
Cleanup
~~~~~~~
When your scripting logic finishes, it's good manners (but not required) to
call the ``closer`` callback:
.. code-block:: python
   from pyramid.paster import bootstrap
   env = bootstrap('/path/to/my/development.ini')
   # .. do stuff ...
   env['closer']()
Setting Up Logging
~~~~~~~~~~~~~~~~~~
By default, :func:`pyramid.paster.bootstrap` does not configure logging
parameters present in the configuration file.  If you'd like to configure
logging based on ``[logger]`` and related sections in the configuration file,
use the following command:
.. code-block:: python
   import logging
   logging.fileConfig('/path/to/my/development.ini')
docs/narr/configuration.rst
@@ -6,22 +6,20 @@
Application Configuration 
=========================
Each deployment of an application written using :app:`Pyramid` implies a
specific *configuration* of the framework itself.  For example, an
application which serves up MP3 files for your listening enjoyment might plug
code into the framework that manages song files, while an application that
manages corporate data might plug in code that manages accounting
information.  The way in which code is plugged in to :app:`Pyramid` for a
specific application is referred to as "configuration".
Most people already understand "configuration" as settings that influence the
operation of an application.  For instance, it's easy to think of the values
in a ``.ini`` file parsed at application startup time as "configuration".
However, if you're reasonably open-minded, it's easy to think of *code* as
configuration too.  Since Pyramid, like most other web application platforms,
is a *framework*, it calls into code that you write (as opposed to a
*library*, which is code that exists purely for you to call).  The act of
plugging application code that you've written into :app:`Pyramid` is also
referred to within this documentation as "configuration"; you are configuring
:app:`Pyramid` to call the code that makes up your application.
Most people understand "configuration" as coarse settings that inform the
high-level operation of a specific application deployment.  For instance,
it's easy to think of the values implied by a ``.ini`` file parsed at
application startup time as "configuration".  :app:`Pyramid` extends this
pattern to application development, using the term "configuration" to express
standardized ways that code gets plugged into a deployment of the framework
itself.  When you plug code into the :app:`Pyramid` framework, you are
"configuring" :app:`Pyramid` to create a particular application.
There are two ways to configure a :app:`Pyramid` application:
:term:`imperative configuration` and :term:`declarative configuration`.  Both
are described below.
.. index::
   single: imperative configuration
@@ -31,8 +29,9 @@
Imperative Configuration
------------------------
Here's one of the simplest :app:`Pyramid` applications, configured
imperatively:
"Imperative configuration" just means configuration done by Python
statements, one after the next.  Here's one of the simplest :app:`Pyramid`
applications, configured imperatively:
.. code-block:: python
   :linenos:
@@ -64,19 +63,17 @@
.. _decorations_and_code_scanning:
Configuration Decorations and Code Scanning
-------------------------------------------
Declarative Configuration
-------------------------
A different mode of configuration gives more *locality of reference* to a
:term:`configuration declaration`.  It's sometimes painful to have all
configuration done in imperative code, because often the code for a single
application may live in many files.  If the configuration is centralized in
one place, you'll need to have at least two files open at once to see the
"big picture": the file that represents the configuration, and the file that
contains the implementation objects referenced by the configuration.  To
avoid this, :app:`Pyramid` allows you to insert :term:`configuration
decoration` statements very close to code that is referred to by the
declaration itself.  For example:
It's sometimes painful to have all configuration done by imperative code,
because often the code for a single application may live in many files.  If
the configuration is centralized in one place, you'll need to have at least
two files open at once to see the "big picture": the file that represents the
configuration, and the file that contains the implementation objects
referenced by the configuration.  To avoid this, :app:`Pyramid` allows you to
insert :term:`configuration decoration` statements very close to code that is
referred to by the declaration itself.  For example:
.. code-block:: python
   :linenos:
@@ -135,6 +132,9 @@
behalf: these calls replace the need to add imperative configuration
statements that don't live near the code being configured.
The combination of :term:`configuration decoration` and the invocation of a
:term:`scan` is collectively known as :term:`declarative configuration`.
In the example above, the scanner translates the arguments to
:class:`~pyramid.view.view_config` into a call to the
:meth:`pyramid.config.Configurator.add_view` method, effectively:
@@ -145,13 +145,10 @@
   config.add_view(hello)
Declarative Configuration
-------------------------
Summary
-------
A third mode of configuration can be employed when you create a
:app:`Pyramid` application named *declarative configuration*.  This mode uses
an XML language known as :term:`ZCML` to represent configuration statements
rather than Python.  ZCML is not built-in to Pyramid, but almost everything
that can be configured imperatively can also be configured via ZCML if you
install the :term:`pyramid_zcml` package.
There are two ways to configure a :app:`Pyramid` application: declaratively
and imperatively.  You can choose the mode you're most comfortable with; both
are completely equivalent.  Examples in this documentation will use both
modes interchangeably.
docs/narr/environment.rst
@@ -8,9 +8,12 @@
   single: debug_all
   single: reload_all
   single: debug settings
   single: debug_routematch
   single: prevent_http_cache
   single: reload settings
   single: default_locale_name
   single: environment variables
   single: Mako environment settings
   single: ini file settings
   single: PasteDeploy settings
@@ -44,14 +47,14 @@
flag is meaningful to Chameleon and Mako templates, as well as most
third-party template rendering extensions.
+---------------------------------+-----------------------------+
| Environment Variable Name       | Config File Setting Name    |
+=================================+=============================+
| ``PYRAMID_RELOAD_TEMPLATES``    |  ``reload_templates``       |
|                                 |                             |
|                                 |                             |
|                                 |                             |
+---------------------------------+-----------------------------+
+---------------------------------+--------------------------------+
| Environment Variable Name       | Config File Setting Name       |
+=================================+================================+
| ``PYRAMID_RELOAD_TEMPLATES``    |  ``pyramid.reload_templates``  |
|                                 |                                |
|                                 |                                |
|                                 |                                |
+---------------------------------+--------------------------------+
Reloading Assets
----------------
@@ -62,7 +65,7 @@
+---------------------------------+-----------------------------+
| Environment Variable Name       | Config File Setting Name    |
+=================================+=============================+
| ``PYRAMID_RELOAD_ASSETS``       |  ``reload_assets``          |
| ``PYRAMID_RELOAD_ASSETS``       |  ``pyramid.reload_assets``  |
|                                 |                             |
|                                 |                             |
|                                 |                             |
@@ -70,7 +73,7 @@
.. note:: For backwards compatibility purposes, aliases can be
   used for configurating asset reloading: ``PYRAMID_RELOAD_RESOURCES`` (envvar)
   and ``reload_resources`` (config file).
   and ``pyramid.reload_resources`` (config file).
Debugging Authorization
-----------------------
@@ -78,14 +81,14 @@
Print view authorization failure and success information to stderr
when this value is true.  See also :ref:`debug_authorization_section`.
+---------------------------------+-----------------------------+
| Environment Variable Name       | Config File Setting Name    |
+=================================+=============================+
| ``PYRAMID_DEBUG_AUTHORIZATION`` |  ``debug_authorization``    |
|                                 |                             |
|                                 |                             |
|                                 |                             |
+---------------------------------+-----------------------------+
+---------------------------------+-----------------------------------+
| Environment Variable Name       | Config File Setting Name          |
+=================================+===================================+
| ``PYRAMID_DEBUG_AUTHORIZATION`` |  ``pyramid.debug_authorization``  |
|                                 |                                   |
|                                 |                                   |
|                                 |                                   |
+---------------------------------+-----------------------------------+
Debugging Not Found Errors
--------------------------
@@ -93,14 +96,14 @@
Print view-related ``NotFound`` debug messages to stderr
when this value is true.  See also :ref:`debug_notfound_section`.
+---------------------------------+-----------------------------+
| Environment Variable Name       | Config File Setting Name    |
+=================================+=============================+
| ``PYRAMID_DEBUG_NOTFOUND``      |  ``debug_notfound``         |
|                                 |                             |
|                                 |                             |
|                                 |                             |
+---------------------------------+-----------------------------+
+---------------------------------+------------------------------+
| Environment Variable Name       | Config File Setting Name     |
+=================================+==============================+
| ``PYRAMID_DEBUG_NOTFOUND``      |  ``pyramid.debug_notfound``  |
|                                 |                              |
|                                 |                              |
|                                 |                              |
+---------------------------------+------------------------------+
Debugging Route Matching
------------------------
@@ -108,14 +111,33 @@
Print debugging messages related to :term:`url dispatch` route matching when
this value is true.  See also :ref:`debug_routematch_section`.
+---------------------------------+-----------------------------+
| Environment Variable Name       | Config File Setting Name    |
+=================================+=============================+
| ``PYRAMID_DEBUG_ROUTEMATCH``    |  ``debug_routematch``       |
|                                 |                             |
|                                 |                             |
|                                 |                             |
+---------------------------------+-----------------------------+
+---------------------------------+--------------------------------+
| Environment Variable Name       | Config File Setting Name       |
+=================================+================================+
| ``PYRAMID_DEBUG_ROUTEMATCH``    |  ``pyramid.debug_routematch``  |
|                                 |                                |
|                                 |                                |
|                                 |                                |
+---------------------------------+--------------------------------+
.. _preventing_http_caching:
Preventing HTTP Caching
------------------------
Prevent the ``http_cache`` view configuration argument from having any effect
globally in this process when this value is true.  No http caching-related
response headers will be set by the Pyramid ``http_cache`` view configuration
feature when this is true.  See also :ref:`influencing_http_caching`.
+---------------------------------+----------------------------------+
| Environment Variable Name       | Config File Setting Name         |
+=================================+==================================+
| ``PYRAMID_PREVENT_HTTP_CACHE``  |  ``pyramid.prevent_http_cache``  |
|                                 |                                  |
|                                 |                                  |
|                                 |                                  |
+---------------------------------+----------------------------------+
Debugging All
-------------
@@ -125,7 +147,7 @@
+---------------------------------+-----------------------------+
| Environment Variable Name       | Config File Setting Name    |
+=================================+=============================+
| ``PYRAMID_DEBUG_ALL``           |  ``debug_all``              |
| ``PYRAMID_DEBUG_ALL``           |  ``pyramid.debug_all``      |
|                                 |                             |
|                                 |                             |
|                                 |                             |
@@ -139,7 +161,7 @@
+---------------------------------+-----------------------------+
| Environment Variable Name       | Config File Setting Name    |
+=================================+=============================+
| ``PYRAMID_RELOAD_ALL``          |  ``reload_all``             |
| ``PYRAMID_RELOAD_ALL``          |  ``pyramid.reload_all``     |
|                                 |                             |
|                                 |                             |
|                                 |                             |
@@ -154,14 +176,14 @@
:term:`locale negotiator` is not registered.  See also
:ref:`localization_deployment_settings`.
+---------------------------------+-----------------------------+
| Environment Variable Name       | Config File Setting Name    |
+=================================+=============================+
| ``PYRAMID_DEFAULT_LOCALE_NAME`` |  ``default_locale_name``    |
|                                 |                             |
|                                 |                             |
|                                 |                             |
+---------------------------------+-----------------------------+
+---------------------------------+-----------------------------------+
| Environment Variable Name       | Config File Setting Name          |
+=================================+===================================+
| ``PYRAMID_DEFAULT_LOCALE_NAME`` |  ``pyramid.default_locale_name``  |
|                                 |                                   |
|                                 |                                   |
|                                 |                                   |
+---------------------------------+-----------------------------------+
.. _mako_template_renderer_settings:
@@ -324,8 +346,8 @@
  [app:main]
  use = egg:MyProject#app
  reload_templates = true
  debug_authorization = true
  pyramid.reload_templates = true
  pyramid.debug_authorization = true
You can also use environment variables to accomplish the same purpose
for settings documented as such.  For example, you might start your
@@ -342,18 +364,18 @@
application's ``.ini`` file.
If you want to turn all ``debug`` settings (every setting that starts
with ``debug_``). on in one fell swoop, you can use
with ``pyramid.debug_``). on in one fell swoop, you can use
``PYRAMID_DEBUG_ALL=1`` as an environment variable setting or you may use
``debug_all=true`` in the config file.  Note that this does not affect
settings that do not start with ``debug_*`` such as
``reload_templates``.
``pyramid.debug_all=true`` in the config file.  Note that this does not affect
settings that do not start with ``pyramid.debug_*`` such as
``pyramid.reload_templates``.
If you want to turn all ``reload`` settings (every setting that starts
with ``reload_``) on in one fell swoop, you can use
If you want to turn all ``pyramid.reload`` settings (every setting that starts
with ``pyramid.reload_``) on in one fell swoop, you can use
``PYRAMID_RELOAD_ALL=1`` as an environment variable setting or you may use
``reload_all=true`` in the config file.  Note that this does not
affect settings that do not start with ``reload_*`` such as
``debug_notfound``.
``pyramid.reload_all=true`` in the config file.  Note that this does not
affect settings that do not start with ``pyramid.reload_*`` such as
``pyramid.debug_notfound``.
.. note::
   Specifying configuration settings via environment variables is generally
@@ -370,35 +392,38 @@
Understanding the Distinction Between ``reload_templates`` and ``reload_assets``
--------------------------------------------------------------------------------
The difference between ``reload_assets`` and ``reload_templates`` is a bit
subtle.  Templates are themselves also treated by :app:`Pyramid` as asset
files (along with other static files), so the distinction can be confusing.
It's helpful to read :ref:`overriding_assets_section` for some context
about assets in general.
The difference between ``pyramid.reload_assets`` and
``pyramid.reload_templates`` is a bit subtle. Templates are themselves also
treated by :app:`Pyramid` as asset files (along with other static files), so the
distinction can be confusing.  It's helpful to read
:ref:`overriding_assets_section` for some context about assets in general.
When ``reload_templates`` is true, :app:`Pyramid` takes advantage of the
When ``pyramid.reload_templates`` is true, :app:`Pyramid` takes advantage of the
underlying templating systems' ability to check for file modifications to an
individual template file.  When ``reload_templates`` is true but
``reload_assets`` is *not* true, the template filename returned by the
individual template file.  When ``pyramid.reload_templates`` is true but
``pyramid.reload_assets`` is *not* true, the template filename returned by the
``pkg_resources`` package (used under the hood by asset resolution) is cached
by :app:`Pyramid` on the first request.  Subsequent requests for the same
template file will return a cached template filename.  The underlying
templating system checks for modifications to this particular file for every
request.  Setting ``reload_templates`` to ``True`` doesn't affect performance
dramatically (although it should still not be used in production because it
has some effect).
request.  Setting ``pyramid.reload_templates`` to ``True`` doesn't affect
performance dramatically (although it should still not be used in production
because it has some effect).
However, when ``reload_assets`` is true, :app:`Pyramid` will not cache the
template filename, meaning you can see the effect of changing the content of
an overridden asset directory for templates without restarting the server
However, when ``pyramid.reload_assets`` is true, :app:`Pyramid` will not cache
the template filename, meaning you can see the effect of changing the content
of an overridden asset directory for templates without restarting the server
after every change.  Subsequent requests for the same template file may
return different filenames based on the current state of overridden asset
directories. Setting ``reload_assets`` to ``True`` affects performance
directories. Setting ``pyramid.reload_assets`` to ``True`` affects performance
*dramatically*, slowing things down by an order of magnitude for each
template rendering.  However, it's convenient to enable when moving files
around in overridden asset directories. ``reload_assets`` makes the system
*very slow* when templates are in use.  Never set ``reload_assets`` to
``True`` on a production system.
around in overridden asset directories. ``pyramid.reload_assets`` makes the
system *very slow* when templates are in use.  Never set
``pyramid.reload_assets`` to ``True`` on a production system.
.. index::
   par: settings; adding custom
.. _adding_a_custom_setting:
docs/narr/firstapp.rst
@@ -1,3 +1,6 @@
.. index::
   single: hello world program
.. _firstapp_chapter:
Creating Your First :app:`Pyramid` Application
@@ -12,8 +15,7 @@
Hello World, Goodbye World
--------------------------
Here's one of the very simplest :app:`Pyramid` applications, configured
imperatively:
Here's one of the very simplest :app:`Pyramid` applications:
.. code-block:: python
   :linenos:
@@ -151,7 +153,7 @@
       app = config.make_wsgi_app()
       serve(app, host='0.0.0.0')
Let's break this down this piece-by-piece.
Let's break this down piece-by-piece.
Configurator Construction
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -180,7 +182,7 @@
:class:`~pyramid.config.Configurator` class.  The resulting ``config`` object
represents an API which the script uses to configure this particular
:app:`Pyramid` application.  Methods called on the Configurator will cause
registrations to be made in a :term:`application registry` associated with
registrations to be made in an :term:`application registry` associated with
the application.
.. _adding_configuration:
@@ -320,6 +322,3 @@
For more information about :term:`view configuration`, see
:ref:`view_config_chapter`.
An example of using *declarative* configuration (:term:`ZCML`) instead of
imperative configuration to create a similar "hello world" is available
within the documentation for :term:`pyramid_zcml`.
docs/narr/hooks.rst
@@ -61,8 +61,8 @@
   caused the not found view to be called.  The value of
   ``request.exception.message`` will be a value explaining why the not found
   error was raised.  This message will be different when the
   ``debug_notfound`` environment setting is true than it is when it is
   false.
   ``pyramid.debug_notfound`` environment setting is true than it is when it
   is false.
.. warning:: When a NotFound view callable accepts an argument list as
   described in :ref:`request_and_context_view_definitions`, the ``context``
@@ -128,8 +128,8 @@
   ``request.exception.message`` will be a value explaining why the forbidden
   was raised and ``request.exception.result`` will be extended information
   about the forbidden exception.  These messages will be different when the
   ``debug_authorization`` environment setting is true than it is when it is
   false.
   ``pyramid.debug_authorization`` environment setting is true than it is when
   it is false.
.. index::
   single: request factory
@@ -218,7 +218,7 @@
when adding renderer global values exists in :ref:`adding_renderer_globals`.
.. index::
   single: renderer globals
   single: adding renderer globals
.. _adding_renderer_globals:
@@ -284,8 +284,8 @@
Unlike many other web frameworks, :app:`Pyramid` does not eagerly create a
global response object.  Adding a :term:`response callback` allows an
application to register an action to be performed against a response object
once it is created, usually in order to mutate it.
application to register an action to be performed against whatever response
object is returned by a view, usually in order to mutate the response.
The :meth:`pyramid.request.Request.add_response_callback` method is used to
register a response callback.
@@ -528,6 +528,7 @@
.. index::
   single: IResponse
   single: special view responses
.. _using_iresponse:
@@ -536,7 +537,8 @@
It is possible to control how Pyramid treats the result of calling a view
callable on a per-type basis by using a hook involving
:meth:`pyramid.config.Configurator.add_response_adapter`.
:meth:`pyramid.config.Configurator.add_response_adapter` or the
:class:`~pyramid.response.response_adapter` decorator.
.. note:: This feature is new as of Pyramid 1.1.
@@ -600,6 +602,9 @@
:class:`pyramid.interfaces.IResponse` and you'll have to ensure that it's
marked up with ``zope.interface.implements(IResponse)``:
.. code-block:: python
   :linenos:
   from pyramid.interfaces import IResponse
   from zope.interface import implements
@@ -619,6 +624,29 @@
startup time, as by their nature, instances of this class (and instances of
subclasses of the class) will natively provide IResponse.  The adapter
registered for ``webob.Response`` simply returns the response object.
Instead of using :meth:`pyramid.config.Configurator.add_response_adapter`,
you can use the :class:`pyramid.response.response_adapter` decorator:
.. code-block:: python
   :linenos:
   from pyramid.response import Response
   from pyramid.response import response_adapter
   @response_adapter(str)
   def string_response_adapter(s):
       response = Response(s)
       return response
The above example, when scanned, has the same effect as:
.. code-block:: python
   config.add_response_adapter(string_response_adapter, str)
The :class:`~pyramid.response.response_adapter` decorator will have no effect
until activated by a :term:`scan`.
.. index::
   single: view mapper
@@ -800,3 +828,260 @@
For full details, please read the `Venusian documentation
<http://docs.repoze.org/venusian>`_.
.. _registering_tweens:
Registering "Tweens"
--------------------
.. note:: Tweens are a feature which were added in Pyramid 1.1.1.  They are
   not available in any previous version.
A :term:`tween` (a contraction of the word "between") is a bit of code that
sits between the Pyramid router's main request handling function and the
upstream WSGI component that uses :app:`Pyramid` as its "app".  This is a
feature that may be used by Pyramid framework extensions, to provide, for
example, Pyramid-specific view timing support bookkeeping code that examines
exceptions before they are returned to the upstream WSGI application.  Tweens
behave a bit like :term:`WSGI` middleware but they have the benefit of
running in a context in which they have access to the Pyramid
:term:`application registry` as well as the Pyramid rendering machinery.
To make use of tweens, you must construct a "tween factory".  A tween factory
must be a callable (or a :term:`dotted Python name` to such a callable) which
accepts two arguments: ``handler`` and ``registry``.  ``handler`` will be the
either the main Pyramid request handling function or another tween (if more
than one tween is configured into the request handling chain).  ``registry``
will be the Pyramid :term:`application registry` represented by this
Configurator.  A tween factory must return a tween when it is called.
A tween is a callable which accepts a :term:`request` object and returns a
two-tuple a :term:`response` object.
Once you've created a tween factory, you can register it into the implicit
tween chain using the :meth:`pyramid.config.Configurator.add_tween` method.
Here's an example creating a tween factory and registering it:
.. code-block:: python
   :linenos:
    import time
    from pyramid.settings import asbool
    import logging
    log = logging.getLogger(__name__)
    def timing_tween_factory(handler, registry):
        if asbool(registry.settings.get('do_timing')):
            # if timing support is enabled, return a wrapper
            def timing_tween(request):
                start = time.time()
                try:
                    response = handler(request)
                finally:
                    end = time.time()
                    log.debug('The request took %s seconds' %
                              (end - start))
                return response
            return timing_tween
        # if timing support is not enabled, return the original
        # handler
        return handler
    from pyramid.config import Configurator
    config = Configurator()
    config.add_tween(timing_tween_factory)
The ``request`` argument to a tween will be the request created by Pyramid's
router when it receives a WSGI request.
If more than one call to :meth:`pyramid.config.Configurator.add_tween` is
made within a single application configuration, the tweens will be chained
together at application startup time.  The *first* tween factory added via
``add_tween`` will be called with the Pyramid exception view tween factory as
its ``handler`` argument, then the tween factory added directly after that
one will be called with the result of the first tween factory as its
``handler`` argument, and so on, ad infinitum until all tween factories have
been called. The Pyramid router will use the outermost tween produced by this
chain (the tween generated by the very last tween factory added) as its
request handler function.  For example:
.. code-block:: python
   :linenos:
    from pyramid.config import Configurator
    config = Configurator()
    config.add_tween('myapp.tween_factory1')
    config.add_tween('myapp.tween_factory2')
The above example will generate an implicit tween chain that looks like
this::
    INGRESS (implicit)
    myapp.tween_factory2
    myapp.tween_factory1
    pyramid.tweens.excview_tween_factory (implicit)
    MAIN (implicit)
By default, as described above, the ordering of the chain is controlled
entirely by the relative ordering of calls to
:meth:`pyramid.config.Configurator.add_tween`.  However, the caller of
add_tween can provide an optional hint that can influence the implicit tween
chain ordering by supplying ``under`` or ``over`` (or both) arguments to
:meth:`~pyramid.config.Configurator.add_tween`.  These hints are only used
used when an explicit tween chain is not used (when the ``pyramid.tweens``
configuration value is not set).
Allowable values for ``under`` or ``over`` (or both) are:
- ``None`` (the default).
- A :term:`dotted Python name` to a tween factory: a string representing the
  predicted dotted name of a tween factory added in a call to ``add_tween``
  in the same configuration session.
- A "tween alias": a string representing the predicted value of ``alias`` in
  a separate call to ``add_tween`` in the same configuration session
- One of the constants :attr:`pyramid.tweens.MAIN`,
  :attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`.
Effectively, ``under`` means "closer to the main Pyramid application than",
``over`` means "closer to the request ingress than".
For example, the following call to
:meth:`~pyramid.config.Configurator.add_tween` will attempt to place the
tween factory represented by ``myapp.tween_factory`` directly 'above' (in
``paster ptweens`` order) the main Pyramid request handler.
.. code-block:: python
   :linenos:
   import pyramid.tweens
   config.add_tween('myapp.tween_factory', over=pyramid.tweens.MAIN)
The above example will generate an implicit tween chain that looks like
this::
    INGRESS (implicit)
    pyramid.tweens.excview_tween_factory (implicit)
    myapp.tween_factory
    MAIN (implicit)
Likewise, calling the following call to
:meth:`~pyramid.config.Configurator.add_tween` will attempt to place this
tween factory 'above' the main handler but 'below' a separately added tween
factory:
.. code-block:: python
   :linenos:
   import pyramid.tweens
   config.add_tween('myapp.tween_factory1',
                    over=pyramid.tweens.MAIN)
   config.add_tween('myapp.tween_factory2',
                    over=pyramid.tweens.MAIN,
                    under='myapp.tween_factory1')
The above example will generate an implicit tween chain that looks like
this::
    INGRESS (implicit)
    pyramid.tweens.excview_tween_factory (implicit)
    myapp.tween_factory1
    myapp.tween_factory2
    MAIN (implicit)
Specifying neither ``over`` nor ``under`` is equivalent to specifying
``under=INGRESS``.
If an ``under`` or ``over`` value is provided that does not match a tween
factory dotted name or alias in the current configuration, that value will be
ignored.  It is not an error to provide an ``under`` or ``over`` value that
matches an unused tween factory.
:meth:`~pyramid.config.Configurator.add_tween` also accepts an ``alias``
argument.  If ``alias`` is not ``None``, should be a string.  The string will
represent a value that other callers of ``add_tween`` may pass as an
``under`` and ``over`` argument instead of a dotted name to a tween factory.
For example:
.. code-block:: python
   :linenos:
   import pyramid.tweens
   config.add_tween('myapp.tween_factory1',
                    alias='one'
                    over=pyramid.tweens.MAIN)
   config.add_tween('myapp.tween_factory2',
                    alias='two'
                    over=pyramid.tweens.MAIN,
                    under='one')
Alias names are only useful in relation to ``under`` and ``over`` values.
They cannot be used in explicit tween chain configuration, or anywhere else.
Implicit tween ordering is obviously only best-effort.  Pyramid will attempt
to provide an implicit order of tweens as best it can using hints provided by
calls to :meth:`~pyramid.config.Configurator.add_tween`, but because it's
only best-effort, if very precise tween ordering is required, the only
surefire way to get it is to use an explicit tween order.  The deploying user
can override the implicit tween inclusion and ordering implied by calls to
:meth:`~pyramid.config.Configurator.add_tween` entirely by using the
``pyramid.tweens`` settings value.  When used, this settings value must be a
list of Python dotted names which will override the ordering (and inclusion)
of tween factories in the implicit tween chain.  For example:
.. code-block:: ini
   :linenos:
   [app:main]
   use = egg:MyApp
   pyramid.reload_templates = true
   pyramid.debug_authorization = false
   pyramid.debug_notfound = false
   pyramid.debug_routematch = false
   pyramid.debug_templates = true
   pyramid.tweens = myapp.my_cool_tween_factory
                    pyramid.tweens.excview_tween_factory
In the above configuration, calls made during configuration to
:meth:`pyramid.config.Configurator.add_tween` are ignored, and the user is
telling the system to use the tween factories he has listed in the
``pyramid.tweens`` configuration setting (each is a :term:`dotted Python
name` which points to a tween factory) instead of any tween factories added
via :meth:`pyramid.config.Configurator.add_tween`.  The *first* tween factory
in the ``pyramid.tweens`` list will be used as the producer of the effective
:app:`Pyramid` request handling function; it will wrap the tween factory
declared directly "below" it, ad infinitum.  The "main" Pyramid request
handler is implicit, and always "at the bottom".
.. note:: Pyramid's own :term:`exception view` handling logic is implemented
   as a tween factory function: :func:`pyramid.tweens.excview_tween_factory`.
   If Pyramid exception view handling is desired, and tween factories are
   specified via the ``pyramid.tweens`` configuration setting, the
   :func:`pyramid.tweens.excview_tween_factory` function must be added to the
   ``pyramid.tweens`` configuration setting list explicitly.  If it is not
   present, Pyramid will not perform exception view handling.
Pyramid will prevent the same tween factory from being added to the tween
chain more than once using configuration conflict detection.  If you wish to
add the same tween factory more than once in a configuration, you should
either: a) use a tween factory that is an instance rather than a function or
class, b) use a function or class as a tween factory with the same logic as
the other tween factory it conflicts with but with a different ``__name__``
attribute or c) call :meth:`pyramid.config.Configurator.commit` between calls
to :meth:`pyramid.config.Configurator.add_tween`.
If a cycle is detected in implicit tween ordering when ``over`` and ``under``
are used in any call to "add_tween", an exception will be raised at startup
time.
The ``paster ptweens`` command-line utility can be used to report the current
implict and explicit tween chains used by an application.  See
:ref:`displaying_tweens`.
docs/narr/hybrid.rst
@@ -79,6 +79,9 @@
calls to :meth:`pyramid.config.Configurator.add_route` in its startup
code.
.. index::
   single: hybrid applications
Hybrid Applications
-------------------
@@ -176,6 +179,9 @@
   :ref:`route_factories`.  Both the global root factory and default
   root factory were explained previously within
   :ref:`the_resource_tree`.
.. index::
   pair: hybrid applications; *traverse route pattern
.. _using_traverse_in_a_route_pattern:
@@ -400,6 +406,9 @@
the global root, or the object returned by the ``factory`` associated
with this route).
.. index::
   pair: hybrid applications; global views
Making Global Views Match
+++++++++++++++++++++++++
@@ -420,6 +429,7 @@
   config.add_view('myproject.views.bazbuz', name='bazbuz')
.. index::
   pair: hybrid applications; *subpath
   single: route subpath
   single: subpath (route)
@@ -431,8 +441,9 @@
There are certain extremely rare cases when you'd like to influence the
traversal :term:`subpath` when a route matches without actually performing
traversal.  For instance, the :func:`pyramid.wsgi.wsgiapp2` decorator and the
:class:`pyramid.view.static` helper attempt to compute ``PATH_INFO`` from the
request's subpath, so it's useful to be able to influence this value.
:class:`pyramid.static.static_view` helper attempt to compute ``PATH_INFO``
from the request's subpath when its ``use_subpath`` argument is ``True``, so
it's useful to be able to influence this value.
When ``*subpath`` exists in a pattern, no path is actually traversed,
but the traversal algorithm will return a :term:`subpath` list implied
@@ -442,12 +453,19 @@
.. code-block:: python
   :linenos:
   config.add_route('static', '/static/*subpath')
   config.add_view('mypackage.views.static_view', route_name='static')
   from pryamid.static import static_view
Where ``mypackage.views.static_view`` is an instance of
:class:`pyramid.view.static`.  This effectively tells the static helper to
traverse everything in the subpath as a filename.
   www = static_view('mypackage:static', use_subpath=True)
   config.add_route('static', '/static/*subpath')
   config.add_view(www, route_name='static')
``mypackage.views.www`` is an instance of
:class:`pyramid.static.static_view`.  This effectively tells the static
helper to traverse everything in the subpath as a filename.
.. index::
   pair: hybrid applications; corner cases
Corner Cases
------------
docs/narr/i18n.rst
@@ -353,6 +353,9 @@
``myapplication/locale/myapplication.pot``.
.. index::
   single: translation domains
Translation Domains
+++++++++++++++++++
@@ -494,6 +497,8 @@
.. index::
   single: localizer
   single: get_localizer
   single: translation
   single: pluralization
Using a Localizer
-----------------
@@ -618,7 +623,7 @@
This returns the locale name negotiated by the currently active
:term:`locale negotiator` or the :term:`default locale name` if the
locale negotiator returns ``None``.  You can change the default locale
name by changing the ``default_locale_name`` setting; see
name by changing the ``pyramid.default_locale_name`` setting; see
:ref:`default_locale_name_setting`.
Once :func:`~pyramid.i18n.get_locale_name` is first run, the locale
@@ -744,6 +749,9 @@
equivalent.  For those, you can always use the more manual translation
facility described in :ref:`performing_a_translation`.
.. index::
   single: Mako i18n
Mako Pyramid I18N Support
-------------------------
@@ -760,7 +768,7 @@
Localization-Related Deployment Settings
----------------------------------------
A :app:`Pyramid` application will have a ``default_locale_name``
A :app:`Pyramid` application will have a ``pyramid.default_locale_name``
setting.  This value represents the :term:`default locale name` used
when the :term:`locale negotiator` returns ``None``.  Pass it to the
:mod:`~pyramid.config.Configurator` constructor at startup
@@ -770,9 +778,9 @@
   :linenos:
   from pyramid.config import Configurator
   config = Configurator(settings={'default_locale_name':'de'})
   config = Configurator(settings={'pyramid.default_locale_name':'de'})
You may alternately supply a ``default_locale_name`` via an
You may alternately supply a ``pyramid.default_locale_name`` via an
application's Paster ``.ini`` file:
.. code-block:: ini
@@ -780,10 +788,10 @@
   [app:main]
   use = egg:MyProject#app
   reload_templates = true
   debug_authorization = false
   debug_notfound = false
   default_locale_name = de
   pyramid.reload_templates = true
   pyramid.debug_authorization = false
   pyramid.debug_notfound = false
   pyramid.default_locale_name = de
If this value is not supplied via the Configurator constructor or via
a Paste config file, it will default to ``en``.
@@ -796,7 +804,10 @@
   from pyramid.threadlocal import get_current_registry
   settings = get_current_registry().settings
   default_locale_name = settings['default_locale_name']
   default_locale_name = settings['pyramid.default_locale_name']
.. index::
   single: detecting langauges
"Detecting" Available Languages
-------------------------------
@@ -857,6 +868,9 @@
   pair: locale; negotiator
   single: translation directory
.. index::
   pair: activating; translation
.. _activating_translation:
Activating Translation
@@ -868,6 +882,9 @@
- add at least one :term:`translation directory` to your application.
- ensure that your application sets the :term:`locale name` correctly.
.. index::
   pair: translation directory; adding
.. _adding_a_translation_directory:
@@ -906,6 +923,9 @@
if both translation directories contain translations for the same
locale and :term:`translation domain`.
.. index::
   pair: setting; locale
Setting the Locale
~~~~~~~~~~~~~~~~~~
@@ -936,6 +956,9 @@
   function into that application as required.  See
   :ref:`custom_locale_negotiator`.
.. index::
   single: locale negotiator
.. _locale_negotiators:
Locale Negotiators
docs/narr/install.rst
@@ -9,21 +9,19 @@
Before You Install
------------------
You will need `Python <http://python.org>`_ version 2.4 or better to
You will need `Python <http://python.org>`_ version 2.5 or better to
run :app:`Pyramid`.  
.. sidebar:: Python Versions
    As of this writing, :app:`Pyramid` has been tested under Python
    2.4.6, Python 2.5.4 and Python 2.6.2, and Python 2.7.  To ensure
    backwards compatibility, development of :app:`Pyramid` is
    currently done primarily under Python 2.4 and Python 2.5.
    :app:`Pyramid` does not run under any version of Python before
    2.4, and does not yet run under Python 3.X.
    As of this writing, :app:`Pyramid` has been tested under Python 2.5.5,
    Python 2.6.6, and Python 2.7.2.  :app:`Pyramid` does not run under any
    version of Python before 2.5, and does not yet run under Python 3.X.
:app:`Pyramid` is known to run on all popular Unix-like systems such as
Linux, MacOS X, and FreeBSD as well as on Windows platforms.  It is also
known to run on Google's App Engine and :term:`Jython`.
known to run on Google's App Engine, :term:`PyPy` (1.5), and :term:`Jython`
(2.5.2).
:app:`Pyramid` installation does not require the compilation of any
C code, so you need only a Python interpreter that meets the
@@ -36,6 +34,9 @@
you can either install Python using your operating system's package
manager *or* you can install Python from source fairly easily on any
UNIX system that has development tools.
.. index::
   pair: install; Python (from package, UNIX)
Package Manager Method
++++++++++++++++++++++
@@ -54,6 +55,9 @@
Once these steps are performed, the Python interpreter will usually be
invokable via ``python2.6`` from a shell prompt.
.. index::
   pair: install; Python (from source, UNIX)
Source Compile Method
+++++++++++++++++++++
@@ -76,9 +80,9 @@
On Mac OS X, installing `XCode
<http://developer.apple.com/tools/xcode/>`_ has much the same effect.
Once you've got development tools installed on your system, On the
same system, to install a Python 2.6 interpreter from *source*, use
the following commands:
Once you've got development tools installed on your system, you can
install a Python 2.6 interpreter from *source*, on the same system,
using the following commands:
.. code-block:: text
@@ -97,6 +101,9 @@
Once these steps are performed, the Python interpreter will be
invokable via ``$HOME/opt/Python-2.6.4/bin/python`` from a shell
prompt.
.. index::
   pair: install; Python (from package, Windows)
If You Don't Yet Have A Python Interpreter (Windows)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -143,15 +150,15 @@
.. code-block:: text
   [chrism@vitaminf pyramid]$ python
   Python 2.4.5 (#1, Aug 29 2008, 12:27:37)
   [GCC 4.0.1 (Apple Inc. build 5465)] on darwin
   Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32)
   [GCC 4.4.3] on linux2
   Type "help", "copyright", "credits" or "license" for more information.
   >>> import setuptools
If running ``import setuptools`` does not raise an ``ImportError``, it
means that setuptools is already installed into your Python
interpreter.  If ``import setuptools`` fails, you will need to install
setuptools manually.  Note that above we're using a Python 2.4-series
setuptools manually.  Note that above we're using a Python 2.6-series
interpreter on Mac OS X; your output may differ if you're using a
later Python version or a different platform.
@@ -183,7 +190,7 @@
   $ sudo python ez_setup.py
.. index::
   single: virtualenv
   pair: install; virtualenv
Installing the ``virtualenv`` Package
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -207,6 +214,7 @@
.. index::
   single: virtualenv
   pair: Python; virtual environment
Creating the Virtual Python Environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -315,6 +323,9 @@
:ref:`appengine_tutorial` documents the steps required to install a
:app:`Pyramid` application on Google App Engine.
.. index::
   single: installing on Jython
Installing :app:`Pyramid` on Jython
--------------------------------------
docs/narr/introduction.rst
@@ -10,11 +10,8 @@
==============================
:app:`Pyramid` is a general, open source, Python web application development
*framework*. Its primary goal is to make it easier for a developer to create
web applications. The type of application being created could be a
spreadsheet, a corporate intranet, or a social networking platform; Pyramid's
generality enables it to be used to build an unconstrained variety of web
applications.
*framework*. Its primary goal is to make it easier for a Python developer to
create web applications.
.. sidebar:: Frameworks vs. Libraries
@@ -33,34 +30,30 @@
   own via a set of libraries if the framework provides a set of
   facilities that fits your application requirements.
The first release of Pyramid's predecessor (named :mod:`repoze.bfg`) was made
in July of 2008. We have worked hard to ensure that Pyramid continues to
follow the design and engineering principles that we consider to be the core
characteristics of a successful framework:
Pyramid attempts to follow these design and engineering principles:
Simplicity
  :app:`Pyramid` takes a *"pay only for what you eat"* approach.  This means
  that you can get results even if you have only a partial understanding of
  :app:`Pyramid`.  It doesn’t force you to use any particular technology to
  produce an application, and we try to keep the core set of concepts that
  you need to understand to a minimum.
  :app:`Pyramid` takes a *"pay only for what you eat"* approach.  You can get
  results even if you have only a partial understanding of :app:`Pyramid`.
  It doesn’t force you to use any particular technology to produce an
  application, and we try to keep the core set of concepts that you need to
  understand to a minimum.
Minimalism
  :app:`Pyramid` concentrates on providing fast, high-quality solutions to
  the fundamental problems of creating a web application: the mapping of URLs
  to code, templating, security and serving static assets. We consider these
  to be the core activities that are common to nearly all web applications.
  :app:`Pyramid` tries to solve only the the fundamental problems of creating
  a web application: the mapping of URLs to code, templating, security and
  serving static assets. We consider these to be the core activities that are
  common to nearly all web applications.
Documentation
  Pyramid's minimalism means that it is relatively easy for us to maintain
  extensive and up-to-date documentation. It is our goal that no aspect of
  Pyramid remains undocumented.
  Pyramid's minimalism means that it is easier for us to maintain complete
  and up-to-date documentation. It is our goal that no aspect of Pyramid
  is undocumented.
Speed
  :app:`Pyramid` is designed to provide noticeably fast execution for common
  tasks such as templating and simple response generation. Although the
  “hardware is cheap” mantra may appear to offer a ready solution to speed
  problems, the limits of this approach become painfully evident when one
  tasks such as templating and simple response generation. Although “hardware
  is cheap", the limits of this approach become painfully evident when one
  finds him or herself responsible for managing a great many machines.
Reliability
@@ -74,16 +67,14 @@
  open source license <http://repoze.org/license.html>`_.
.. index::
   single: Pylons
   single: Agendaless Consulting
   single: repoze namespace package
   single: Pylons Project
What Is The Pylons Project?
---------------------------
: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://docs.pylonsproject.org>`_
contributors.  The `Pylons Project website <http://pylonsproject.org>`_
includes details about how :app:`Pyramid` relates to the Pylons Project.
.. index::
@@ -96,23 +87,22 @@
:app:`Pyramid` and Other Web Frameworks
------------------------------------------
Until the end of 2010, :app:`Pyramid` was known as :mod:`repoze.bfg`; it was
merged into the Pylons project as :app:`Pyramid` in November of that year.
The first release of Pyramid's predecessor (named :mod:`repoze.bfg`) was made
in July of 2008.  At the end of 2010, we changed the name of
:mod:`repoze.bfg` to :app:`Pyramid`.  It was merged into the Pylons project
as :app:`Pyramid` in November of that year.
:app:`Pyramid` was inspired by :term:`Zope`, :term:`Pylons` (version
1.0) and :term:`Django`.  As a result, :app:`Pyramid` borrows several
concepts and features from each, combining them into a unique web
framework.
Many features of :app:`Pyramid` trace their origins back to
:term:`Zope`.  Like Zope applications, :app:`Pyramid` applications
can be configured via a set of declarative configuration files.  Like
Zope applications, :app:`Pyramid` applications can be easily
extended: if you obey certain constraints, the application you produce
can be reused, modified, re-integrated, or extended by third-party
developers without forking the original application.  The concepts of
:term:`traversal` and declarative security in :app:`Pyramid` were
pioneered first in Zope.
Many features of :app:`Pyramid` trace their origins back to :term:`Zope`.
Like Zope applications, :app:`Pyramid` applications can be easily extended:
if you obey certain constraints, the application you produce can be reused,
modified, re-integrated, or extended by third-party developers without
forking the original application.  The concepts of :term:`traversal` and
declarative security in :app:`Pyramid` were pioneered first in Zope.
The :app:`Pyramid` concept of :term:`URL dispatch` is inspired by the
:term:`Routes` system used by :term:`Pylons` version 1.0.  Like Pylons
@@ -127,15 +117,14 @@
by Django.  :app:`Pyramid` has a documentation culture more like Django's
than like Zope's.
Like :term:`Pylons` version 1.0, but unlike :term:`Zope`, a
:app:`Pyramid` application developer may use completely imperative
code to perform common framework configuration tasks such as adding a
view or a route.  In Zope, :term:`ZCML` is typically required for
similar purposes.  In :term:`Grok`, a Zope-based web framework,
:term:`decorator` objects and class-level declarations are used for
this purpose.  :app:`Pyramid` supports :term:`ZCML` and
decorator-based configuration, but does not require either. See
:ref:`configuration_narr` for more information.
Like :term:`Pylons` version 1.0, but unlike :term:`Zope`, a :app:`Pyramid`
application developer may use completely imperative code to perform common
framework configuration tasks such as adding a view or a route.  In Zope,
:term:`ZCML` is typically required for similar purposes.  In :term:`Grok`, a
Zope-based web framework, :term:`decorator` objects and class-level
declarations are used for this purpose.  :app:`Pyramid` supports :term:`ZCML`
and decorator-based :term:`declarative configuration`, but does not require
either. See :ref:`configuration_narr` for more information.
Also unlike :term:`Zope` and unlike other "full-stack" frameworks such
as :term:`Django`, :app:`Pyramid` makes no assumptions about which
docs/narr/muchadoabouttraversal.rst
@@ -39,6 +39,9 @@
is actually a straightforward metaphor easily comprehended by anyone who's
ever used a run-of-the-mill file system with folders and files.
.. index::
   single: URL dispatch
URL Dispatch
------------
@@ -100,11 +103,11 @@
configuration specified which files would trigger some dynamic code, with the
default case being to just serve the static file.
.. index::
   single: traversal
Traversal (aka Resource Location)
---------------------------------
.. index::
   single: traversal overview
Believe it or not, if you understand how serving files from a file system
works, you understand traversal.  And if you understand that a server might do
@@ -140,6 +143,9 @@
generated anywhere along the way, :app:`Pyramid` will return 404.  (This
isn't precisely true, as you'll see when we learn about view lookup below,
but the basic idea holds.)
.. index::
   single: resource
What Is a "Resource"?
---------------------
@@ -194,6 +200,9 @@
.. note:: See the chapter entitled :ref:`resources_chapter` for a more
   technical overview of resources.
.. index::
   single: view lookup
View Lookup
-----------
docs/narr/project-debug.png
docs/narr/project.png

docs/narr/project.rst
@@ -12,9 +12,9 @@
You'll use a scaffold to create a project, and you'll create your application
logic within a package that lives inside the project.  Even if your
application is extremely simple, it is useful to place code that drives the
application within a package, because a package is more easily extended with
new code.  An application that lives inside a package can also be distributed
more easily than one which does not live within a package.
application within a package, because: 1) a package is more easily extended
with new code and 2) an application that lives inside a package can also be
distributed more easily than one which does not live within a package.
:app:`Pyramid` comes with a variety of scaffolds that you can use to generate
a project.  Each scaffold makes different configuration assumptions about
@@ -99,19 +99,18 @@
   $ bin/paster create -t pyramid_starter
The above command uses the ``paster`` command to create a project using the
``pyramid_starter`` scaffold.  The ``paster create`` command creates project
from a scaffold.  To use a different scaffold, such as
The above command uses the ``paster create`` command to create a project with
the ``pyramid_starter`` scaffold.  To use a different scaffold, such as
``pyramid_routesalchemy``, you'd just change the last argument.  For example:
.. code-block:: text
   $ bin/paster create -t pyramid_routesalchemy
``paster create`` will ask you a single question: the *name* of the
project.  You should use a string without spaces and with only letters
in it.  Here's sample output from a run of ``paster create`` for a
project we name ``MyProject``:
``paster create`` will ask you a single question: the *name* of the project.
You should use a string without spaces and with only letters in it.  Here's
sample output from a run of ``paster create`` for a project we name
``MyProject``:
.. code-block:: text
@@ -177,7 +176,7 @@
The file named ``setup.py`` will be in the root of the paster-generated
project directory.  The ``python`` you're invoking should be the one that
lives in the ``bin`` directory of your virtual Python environment.  Your
terminal's current working directory *must* the the newly created project
terminal's current working directory *must* be the newly created project
directory.  For example:
.. code-block:: text
@@ -194,7 +193,8 @@
This will install a :term:`distribution` representing your project into the
interpreter's library set so it can be found by ``import`` statements and by
:term:`PasteDeploy` commands such as ``paster serve`` and ``paster pshell``.
:term:`PasteDeploy` commands such as ``paster serve``, ``paster pshell``,
``paster proutes`` and ``paster pviews``.
.. index::
   single: running tests
@@ -244,115 +244,10 @@
``pyramid_starter`` scaffold, a single sample test exists.
.. index::
   single: interactive shell
   single: IPython
   single: paster pshell
.. _interactive_shell:
The Interactive Shell
---------------------
Once you've installed your program for development using ``setup.py
develop``, you can use an interactive Python shell to examine your
:app:`Pyramid` project's :term:`resource` and :term:`view` objects from a
Python prompt.  To do so, use your virtualenv's ``paster pshell`` command.
The first argument to ``pshell`` is the path to your application's ``.ini``
file.  The second is the ``app`` section name inside the ``.ini`` file which
points to *your application* as opposed to any other section within the
``.ini`` file.  For example, if your application ``.ini`` file might have a
``[app:MyProject]`` section that looks like so:
.. code-block:: ini
   :linenos:
   [app:MyProject]
   use = egg:MyProject
   reload_templates = true
   debug_authorization = false
   debug_notfound = false
   debug_templates = true
   default_locale_name = en
If so, you can use the following command to invoke a debug shell using the
name ``MyProject`` as a section name:
.. code-block:: text
   [chrism@vitaminf shellenv]$ ../bin/paster pshell development.ini MyProject
   Python 2.4.5 (#1, Aug 29 2008, 12:27:37)
   [GCC 4.0.1 (Apple Inc. build 5465)] on darwin
   Type "help" for more information. "root" is the Pyramid app root object,
   "registry" is the Pyramid registry object.
   >>> root
   <myproject.resources.MyResource object at 0x445270>
   >>> registry
   <Registry myproject>
   >>> registry.settings['debug_notfound']
   False
   >>> from myproject.views import my_view
   >>> from pyramid.request import Request
   >>> r = Request.blank('/')
   >>> my_view(r)
   {'project': 'myproject'}
Two names are made available to the pshell user as globals: ``root`` and
``registry``.  ``root`` is the the object returned by the default :term:`root
factory` in your application.  ``registry`` is the :term:`application
registry` object associated with your project's application (often accessed
within view code as ``request.registry``).
If you have `IPython <http://en.wikipedia.org/wiki/IPython>`_ installed in
the interpreter you use to invoke the ``paster`` command, the ``pshell``
command will use an IPython interactive shell instead of a standard Python
interpreter shell.  If you don't want this to happen, even if you have
IPython installed, you can pass the ``--disable-ipython`` flag to the
``pshell`` command to use a standard Python interpreter shell
unconditionally.
.. code-block:: text
   [chrism@vitaminf shellenv]$ ../bin/paster pshell --disable-ipython \
                                development.ini MyProject
You should always use a section name argument that refers to the actual
``app`` section within the Paste configuration file that points at your
:app:`Pyramid` application *without any middleware wrapping*.  In particular,
a section name is inappropriate as the second argument to ``pshell`` if the
configuration section it names is a ``pipeline`` rather than an ``app``.  For
example, if you have the following ``.ini`` file content:
.. code-block:: ini
   :linenos:
   [app:MyProject]
   use = egg:MyProject
   reload_templates = true
   debug_authorization = false
   debug_notfound = false
   debug_templates = true
   default_locale_name = en
   [pipeline:main]
   pipeline =
       egg:WebError#evalerror
       MyProject
Use ``MyProject`` instead of ``main`` as the section name argument to
``pshell`` against the above ``.ini`` file (e.g. ``paster pshell
development.ini MyProject``).  If you use ``main`` instead, an error will
occur.  Use the most specific reference to your application within the
``.ini`` file possible as the section name argument.
Press ``Ctrl-D`` to exit the interactive shell (or ``Ctrl-Z`` on Windows).
.. index::
   single: running an application
   single: paster serve
   single: reload
   single: startup
   single: mod_wsgi
Running The Project Application
-------------------------------
@@ -398,6 +293,10 @@
configuration file settings that influence startup and runtime behavior, see
:ref:`environment_chapter`.
.. index::
   single: mod_wsgi
   single: WSGI
Viewing the Application
-----------------------
@@ -409,6 +308,45 @@
This is the page shown by default when you visit an unmodified ``paster
create`` -generated ``pyramid_starter`` application in a browser.
If you click on the image shown at the right hand top of the page ("^DT"),
you'll be presented with a debug toolbar that provides various niceties while
you're developing.  This image will float above every HTML page served by
:app:`Pyramid` while you develop an application, and allows you show the
toolbar as necessary.  Click on ``Hide`` to hide the toolbar and show the
image again.
.. image:: project-debug.png
For more information about what the debug toolbar allows you to do, see `the
documentation for pyramid_debugtoolbar
<http://docs.pylonsproject.org/projects/pyramid_debugtoolbar/dev/>`_.
The debug toolbar will not be shown (and all debugging will be turned off)
when you use the ``production.ini`` file instead of the ``development.ini``
ini file to run the application.
You can also turn the debug toolbar off by editing ``development.ini`` and
commenting out the line ``pyramid.include = pyramid_debugtoolbar``.  For
example, instead of:
.. code-block:: ini
   :linenos:
   [app:MyApp]
   ...
   pyramid.include = pyramid_debugtoolbar
Put a hash mark in front of the ``pyramid.include`` line:
.. code-block:: ini
   :linenos:
   [app:MyApp]
   ...
   #pyramid.include = pyramid_debugtoolbar
Then restart the application to see that the toolbar has been turned off.
.. sidebar:: Using an Alternate WSGI Server
@@ -520,14 +458,13 @@
The generated ``development.ini`` file looks like so:
.. latexbroken?
.. literalinclude:: MyProject/development.ini
   :language: ini
   :linenos:
This file contains several "sections" including ``[app:MyProject]``,
``[pipeline:main]``, and ``[server:main]``.
``[pipeline:main]``, ``[server:main]`` and several other sections related to
logging configuration.
The ``[app:MyProject]`` section represents configuration for your
application.  This section name represents the ``MyProject`` application (and
@@ -558,7 +495,7 @@
   point can thus be referred to as a "Paste application factory in the
   ``MyProject`` project which has the entry point named ``main`` where the
   entry point refers to a ``main`` function in the ``mypackage`` module".
   If indeed if you open up the ``__init__.py`` module generated within the
   Indeed, if you open up the ``__init__.py`` module generated within the
   ``myproject`` package, you'll see a ``main`` function.  This is the
   function called by :term:`PasteDeploy` when the ``paster serve`` command
   is invoked against our application.  It accepts a global configuration
@@ -567,27 +504,35 @@
The ``use`` setting is the only setting *required* in the ``[app:MyProject]``
section unless you've changed the callable referred to by the
``egg:MyProject`` entry point to accept more arguments: other settings you
add to this section are passed as keywords arguments to the callable
add to this section are passed as keyword arguments to the callable
represented by this entry point (``main`` in our ``__init__.py`` module).
You can provide startup-time configuration parameters to your application by
adding more settings to this section.
The ``reload_templates`` setting in the ``[app:MyProject]`` section is a
:app:`Pyramid` -specific setting which is passed into the framework.  If it
The ``pyramid.reload_templates`` setting in the ``[app:MyProject]`` section is
a :app:`Pyramid` -specific setting which is passed into the framework.  If it
exists, and its value is ``true``, :term:`Chameleon` and :term:`Mako`
template changes will not require an application restart to be detected.  See
:ref:`reload_templates_section` for more information.
The ``debug_templates`` setting in the ``[app:MyProject]`` section is a
The ``pyramid.debug_templates`` setting in the ``[app:MyProject]`` section is a
:app:`Pyramid` -specific setting which is passed into the framework.  If it
exists, and its value is ``true``, :term:`Chameleon` template exceptions will
contained more detailed and helpful information about the error than when
contain more detailed and helpful information about the error than when
this value is ``false``.  See :ref:`debug_templates_section` for more
information.
.. warning:: The ``reload_templates`` and ``debug_templates`` options should
   be turned off for production applications, as template rendering is slowed
   when either is turned on.
.. warning:: The ``pyramid.reload_templates`` and ``pyramid.debug_templates``
   options should be turned off for production applications, as template
   rendering is slowed when either is turned on.
The ``pyramid.include`` setting in the ``[app:MyProject]`` section tells
Pyramid to "include" configuration from another package.  In this case, the
line ``pyramid.include = pyramid_debugtoolbar`` tells Pyramid to include
configuration from the ``pyramid_debugtoolbar`` package.  This turns on a
debugging panel in development mode which will be shown on the right hand
side of the screen.  Including the debug toolbar will also make it possible
to interactively debug exceptions when an error occurs.
Various other settings may exist in this section having to do with debugging
or influencing runtime behavior of a :app:`Pyramid` application.  See
@@ -611,6 +556,16 @@
  application be nonblocking as all application code will run in its own
  thread, provided by the server you're using.
The sections that live between the markers ``# Begin logging configuration``
and ``# End logging configuration`` represent Python's standard library
:mod:`logging` module configuration for your application.  The sections
between these two markers are passed to the `logging module's config file
configuration engine
<http://docs.python.org/howto/logging.html#configuring-logging>`_ when the
``paster serve`` or ``paster pshell`` commands are executed.  The default
configuration sends application logging output to the standard error output
of your terminal.
See the :term:`PasteDeploy` documentation for more information about other
types of things you can put into this ``.ini`` file, such as other
applications, :term:`middleware` and alternate :term:`WSGI` server
@@ -625,16 +580,21 @@
   to your application's ``main`` function as ``global_config`` (see
   the reference to the ``main`` function in :ref:`init_py`).
.. index::
   single: production.ini
``production.ini``
~~~~~~~~~~~~~~~~~~~
The ``production.ini`` file is a :term:`PasteDeploy` configuration file with
a purpose much like that of ``development.ini``.  However, it disables the
WebError interactive debugger, replacing it with a logger which outputs
exception messages to ``stderr`` by default.  It also turns off template
development options such that templates are not automatically reloaded when
changed, and turns off all debugging options.  You can use this file instead
of ``development.ini`` when you put your application into production.
debug toolbar, replacing it with a logger which outputs exception messages to
``stderr`` by default.  It also turns off template development options such
that templates are not automatically reloaded when changed, and turns off all
debugging options.  It allows you to configure a ``weberror#error_catcher``
section that will cause exceptions to be sent to an email address when they
are uncaught.  You can use this file instead of ``development.ini`` when you
put your application into production.
.. index::
   single: MANIFEST.in
@@ -750,6 +710,9 @@
   setuptools add-on such as ``setuptools-git`` or ``setuptools-hg`` for this
   behavior to work properly.
.. index::
   single: setup.cfg
``setup.cfg``
~~~~~~~~~~~~~
@@ -845,6 +808,9 @@
   Line 12 returns a :term:`WSGI` application to the caller of the function
   (Paste).
.. index::
   single: views.py
``views.py``
~~~~~~~~~~~~
@@ -876,14 +842,14 @@
See :ref:`views_which_use_a_renderer` for more information about how views,
renderers, and templates relate and cooperate.
.. note:: Because our ``development.ini`` has a ``reload_templates =
.. note:: Because our ``development.ini`` has a ``pyramid.reload_templates =
   true`` directive indicating that templates should be reloaded when
   they change, you won't need to restart the application server to
   see changes you make to templates.  During development, this is
   handy.  If this directive had been ``false`` (or if the directive
   did not exist), you would need to restart the application server
   for each template change.  For production applications, you should
   set your project's ``reload_templates`` to ``false`` to increase
   set your project's ``pyramid.reload_templates`` to ``false`` to increase
   the speed at which templates may be rendered.
.. index::
@@ -915,6 +881,9 @@
application uses an instance of :class:`myproject.resources.Root` to
represent the root.
.. index::
   single: static directory
``static``
~~~~~~~~~~
@@ -924,7 +893,7 @@
``templates/mytemplate.pt``
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The single :term:`Chameleon` template exists in the project.  Its contents
The single :term:`Chameleon` template that exists in the project.  Its contents
are too long to show here, but it displays a default page when rendered.  It
is referenced by the call to ``add_view`` as the ``renderer`` attribute in
the ``__init__`` file.  See :ref:`views_which_use_a_renderer` for more
@@ -954,6 +923,9 @@
See :ref:`testing_chapter` for more information about writing :app:`Pyramid`
unit tests.
.. index::
   pair: modifying; package structure
.. _modifying_package_structure:
@@ -1024,4 +996,13 @@
argument which accepts a :term:`dotted Python name` or direct object
reference.
Using the Interactive Shell
---------------------------
It is possible to use a Python interpreter prompt loaded with a similar
configuration as would be loaded if you were running your Pyramid application
via ``paster serve``.  This can be a useful debugging tool.  See
:ref:`interactive_shell` for more details.
docs/narr/renderers.rst
@@ -3,10 +3,10 @@
Renderers
=========
A view needn't *always* return a :term:`Response` object.  If a view
happens to return something which does not implement the Pyramid
Response interface, :app:`Pyramid` will attempt to use a
:term:`renderer` to construct a response.  For example:
A view callable needn't *always* return a :term:`Response` object.  If a view
happens to return something which does not implement the Pyramid Response
interface, :app:`Pyramid` will attempt to use a :term:`renderer` to construct
a response.  For example:
.. code-block:: python
   :linenos:
@@ -228,6 +228,9 @@
using the api of the ``request.response`` attribute.  See
:ref:`request_response_attr`.
.. index::
   pair: renderer; JSONP
.. _jsonp_renderer:
JSONP Renderer
@@ -422,8 +425,9 @@
``templates`` directory of the ``mypackage`` package.
The ``Mako`` template renderer can take additional arguments beyond the
standard ``reload_templates`` setting, see the :ref:`environment_chapter` for
additional :ref:`mako_template_renderer_settings`.
standard ``pyramid.reload_templates`` setting, see the
:ref:`environment_chapter` for additional
:ref:`mako_template_renderer_settings`.
.. index::
   single: response headers (from a renderer)
@@ -522,9 +526,6 @@
  returning various values in the ``response_headerlist``, this is purely a
  convenience.
.. index::
   single: renderer (adding)
.. _adding_and_overriding_renderers:
Adding and Changing Renderers
@@ -549,6 +550,9 @@
The first argument is the renderer name.  The second argument is a reference
to an implementation of a :term:`renderer factory` or a :term:`dotted Python
name` referring to such an object.
.. index::
   pair: renderer; adding
.. _adding_a_renderer:
@@ -676,6 +680,9 @@
to the ``MyJinja2Renderer`` constructor will be the full value that was
set as ``renderer=`` in the view configuration.
.. index::
   pair: renderer; changing
Changing an Existing Renderer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -715,6 +722,9 @@
   config.add_renderer(None, 'mypackage.json_renderer_factory')
.. index::
   pair: renderer; overriding at runtime
Overriding A Renderer At Runtime
--------------------------------
docs/narr/resources.rst
@@ -286,6 +286,9 @@
For more information about generating resource URLs, see the documentation
for :func:`pyramid.url.resource_url`.
.. index::
   pair: resource URL generation; overriding
.. _overriding_resource_url_generation:
Overriding Resource URL Generation
@@ -333,6 +336,9 @@
anchor elements (only path elements) to work best with
:func:`~pyramid.url.resource_url`.
.. index::
   single: resource path generation
Generating the Path To a Resource
---------------------------------
@@ -367,6 +373,9 @@
The presence or absence of a :term:`virtual root` has no impact on the
behavior of :func:`~pyramid.traversal.resource_path`.
.. index::
   pair: resource; finding by path
Finding a Resource by Path
--------------------------
@@ -403,6 +412,9 @@
See the :func:`pyramid.traversal.find_resource` documentation for more
information about resolving a path to a resource.
.. index::
   pair: resource; lineage
Obtaining the Lineage of a Resource
-----------------------------------
@@ -470,6 +482,9 @@
parent (or one of its parent's parents, etc.) is an ancestor.
See :func:`pyramid.location.inside` for more information.
.. index::
   pair: resource; finding root
Finding the Root Resource
-------------------------
@@ -617,6 +632,9 @@
For more information about how resource interfaces can be used by view
configuration, see :ref:`using_resource_interfaces`.
.. index::
   pair: resource; finding by interface or class
Finding a Resource With a Class or Interface in Lineage
-------------------------------------------------------
docs/narr/router.rst
@@ -2,6 +2,7 @@
   single: request processing
   single: request
   single: router
   single: request lifecycle
.. _router_chapter:
docs/narr/security.rst
@@ -168,6 +168,9 @@
to invoke the ``blog_entry_add_view`` view.  If he does not, the
:term:`Forbidden view` will be invoked.
.. index::
   pair: permission; default
.. _setting_a_default_permission:
Setting a Default Permission
@@ -197,9 +200,9 @@
  permission is ignored for that view registration, and the
  view-configuration-named permission is used.
- If a view configuration names an explicit permission as the string
  ``__no_permission_required__``, the default permission is ignored,
  and the view is registered *without* a permission (making it
- If a view configuration names the permission
  :data:`pyramid.security.NO_PERMISSION_REQUIRED`, the default permission
  is ignored, and the view is registered *without* a permission (making it
  available to all callers regardless of their credentials).
.. warning::
@@ -207,11 +210,13 @@
   When you register a default permission, *all* views (even :term:`exception
   view` views) are protected by a permission.  For all views which are truly
   meant to be anonymously accessible, you will need to associate the view's
   configuration with the ``__no_permission_required__`` permission.
   configuration with the :data:`pyramid.security.NO_PERMISSION_REQUIRED`
   permission.
.. index::
   single: ACL
   single: access control list
   pair: resource; ACL
.. _assigning_acls:
@@ -513,7 +518,7 @@
authentication information.
This behavior can also be turned on in the application ``.ini`` file
by setting the ``debug_authorization`` key to ``true`` within the
by setting the ``pyramid.debug_authorization`` key to ``true`` within the
application's configuration section, e.g.:
.. code-block:: ini
@@ -521,7 +526,7 @@
  [app:main]
  use = egg:MyProject#app
  debug_authorization = true
  pyramid.debug_authorization = true
With this debug flag turned on, the response sent to the browser will
also contain security debugging information in its body.
@@ -562,7 +567,7 @@
.. code-block:: python
   :linenos:
   class AuthenticationPolicy(object):
   class IAuthenticationPolicy(object):
       """ An object representing a Pyramid authentication policy. """
       def authenticated_userid(self, request):
docs/narr/sessions.rst
@@ -15,6 +15,9 @@
retrieve data from sessions, and two session-specific features: flash
messages, and cross-site request forgery attack prevention.
.. index::
   single: session factory (default)
.. _using_the_default_session_factory:
Using The Default Session Factory
@@ -64,6 +67,9 @@
   session factory implementation (preferably one which keeps session data on
   the server) for anything but the most basic of applications where "session
   security doesn't matter".
.. index::
   single: session object
Using a Session Object
----------------------
@@ -137,6 +143,7 @@
.. index::
   single: pyramid_beaker
   single: Beaker
   single: session factory (alternates)
.. _using_alternate_session_factories:
@@ -153,7 +160,7 @@
``pyramid_beaker``.
.. index::
   single: session factory
   single: session factory (custom)
Creating Your Own Session Factory
---------------------------------
@@ -183,6 +190,9 @@
log messages for single-time display without having direct access to an HTML
template. The user interface consists of a number of methods of the
:term:`session` object.
.. index::
   single: session.flash
Using the ``session.flash`` Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -222,6 +232,9 @@
``False``, and you attempt to add a message value which is already
present in the queue, it will not be added.
.. index::
   single: session.pop_flash
Using the ``session.pop_flash`` Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -254,6 +267,9 @@
   ['info message']
   >>> request.session.pop_flash()
   []
.. index::
   single: session.peek_flash
Using the ``session.peek_flash`` Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -288,7 +304,7 @@
`Cross-site request forgery
<http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ attacks are a
phenomenon whereby a user with an identity on your website might click on a
URL or button on another website which unwittingly redirects the user to your
URL or button on another website which secretly redirects the user to your
application to perform some command that requires elevated privileges.
You can avoid most of these attacks by making sure that the correct *CSRF
@@ -297,6 +313,9 @@
post.  To use CSRF token support, you must enable a :term:`session factory`
as described in :ref:`using_the_default_session_factory` or
:ref:`using_alternate_session_factories`.
.. index::
   single: session.get_csrf_token
Using the ``session.get_csrf_token`` Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -330,6 +349,9 @@
   if token != request.POST['csrf_token']:
       raise ValueError('CSRF token did not match')
.. index::
   single: session.new_csrf_token
Using the ``session.new_csrf_token`` Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
docs/narr/startup.rst
@@ -53,6 +53,10 @@
   that particular composite to understand how to make it refer to your
   :app:`Pyramid` application.
#. The PasteDeploy framework finds all :mod:`logging` related configuration
   in the ``.ini`` file and uses it to configure the Python standard library
   logging system for this application.
#. The application's *constructor* (named by the entry point reference or
   dotted Python name on the ``use=`` line of the section representing your
   :app:`Pyramid` application) is passed the key/value parameters mentioned
@@ -88,10 +92,10 @@
   In this case, the ``myproject.__init__:main`` function referred to by the
   entry point URI ``egg:MyProject`` (see :ref:`MyProject_ini` for more
   information about entry point URIs, and how they relate to callables),
   will receive the key/value pairs ``{'reload_templates':'true',
   'debug_authorization':'false', 'debug_notfound':'false',
   'debug_routematch':'false', 'debug_templates':'true',
   'default_locale_name':'en'}``.
   will receive the key/value pairs ``{'pyramid.reload_templates':'true',
   'pyramid.debug_authorization':'false', 'pyramid.debug_notfound':'false',
   'pyramid.debug_routematch':'false', 'pyramid.debug_templates':'true',
   'pyramid.default_locale_name':'en'}``.
#. The ``main`` function first constructs a
   :class:`~pyramid.config.Configurator` instance, passing a root resource
@@ -105,14 +109,14 @@
   The ``settings`` dictionary contains all the options in the
   ``[app:MyProject]`` section of our .ini file except the ``use`` option
   (which is internal to Paste) such as ``reload_templates``,
   ``debug_authorization``, etc.
   (which is internal to Paste) such as ``pyramid.reload_templates``,
   ``pyramid.debug_authorization``, etc.
#. The ``main`` function then calls various methods on the an instance of the
   class :class:`~pyramid.config.Configurator` method.  The intent of
   calling these methods is to populate an :term:`application registry`,
   which represents the :app:`Pyramid` configuration related to the
   application.
#. The ``main`` function then calls various methods on the instance of the
   class :class:`~pyramid.config.Configurator` created in the previous step.
   The intent of calling these methods is to populate an
   :term:`application registry`, which represents the :app:`Pyramid`
   configuration related to the application.
#. The :meth:`~pyramid.config.Configurator.make_wsgi_app` method is called.
   The result is a :term:`router` instance.  The router is associated with
@@ -136,6 +140,10 @@
   The server serves the application, and the application is running, waiting
   to receive requests.
.. index::
   pair: settings; deployment
   single: custom settings
.. _deployment_settings:
Deployment Settings
docs/narr/templates.rst
@@ -241,6 +241,9 @@
   single: renderer (template)
.. index::
   pair: renderer; system values
.. _renderer_system_values:
System Values Used During Rendering
@@ -276,6 +279,9 @@
renderer itself, but most template renderers, including Chameleon and
Mako renderers, make these names available as top-level template
variables.
.. index::
   pair: renderer; templates
.. _templates_used_as_renderers:
@@ -426,7 +432,7 @@
renderers, including Chameleon ZPT renderers.
.. index::
   single: sample template
   single: ZPT template (sample)
A Sample ZPT Template
~~~~~~~~~~~~~~~~~~~~~
@@ -448,7 +454,7 @@
      <body>
         <h1 class="title">Welcome to <code>${project}</code>, an
      application generated by the <a
      href="http://docs.pylonsproject.org/projects/pyramid/dev/"
      href="http://docs.pylonsproject.org/projects/pyramid/current/"
         >pyramid</a> web
      application framework.</h1>
      </body>
@@ -596,6 +602,9 @@
extension and my Chameleon text template files with a ``.txt``
extension so that these ``svn:ignore`` patterns work.
.. index::
   pair: debugging; templates
.. _debug_templates_section:
Nicer Exceptions in Chameleon Templates
@@ -623,15 +632,15 @@
  $ PYRAMID_DEBUG_TEMPLATES=1 bin/paster serve myproject.ini
To use a setting in the application ``.ini`` file for the same
purpose, set the ``debug_templates`` key to ``true`` within the
application's configuration section, e.g.:
purpose, set the ``pyramid.debug_templates`` key to ``true`` within
the application's configuration section, e.g.:
.. code-block:: ini
  :linenos:
  [app:MyProject]
  use = egg:MyProject#app
  debug_templates = true
  pyramid.debug_templates = true
With template debugging off, a :exc:`NameError` exception resulting
from rendering a template with an undefined variable
@@ -668,7 +677,7 @@
.. note::
   Turning on ``debug_templates`` has the same effect as using the
   Turning on ``pyramid.debug_templates`` has the same effect as using the
   Chameleon environment variable ``CHAMELEON_DEBUG``.  See `Chameleon
   Environment Variables
   <http://chameleon.repoze.org/docs/latest/config.html#environment-variables>`_
@@ -724,6 +733,9 @@
``mako.directories`` setting and other Mako-related settings that can be
placed into the application's ``ini`` file.
.. index::
   single: Mako template (sample)
A Sample Mako Template
~~~~~~~~~~~~~~~~~~~~~~
@@ -740,7 +752,7 @@
      <body>
         <h1 class="title">Welcome to <code>${project}</code>, an
      application generated by the <a
      href="http://docs.pylonsproject.org/projects/pyramid/dev/"
      href="http://docs.pylonsproject.org/projects/pyramid/current/"
         >pyramid</a> web application framework.</h1>
      </body>
    </html>
@@ -781,7 +793,7 @@
  $ PYRAMID_RELOAD_TEMPLATES=1 bin/paster serve myproject.ini
To use a setting in the application ``.ini`` file for the same
purpose, set the ``reload_templates`` key to ``true`` within the
purpose, set the ``pyramid.reload_templates`` key to ``true`` within the
application's configuration section, e.g.:
.. code-block:: ini
@@ -789,7 +801,7 @@
  [app:main]
  use = egg:MyProject#app
  reload_templates = true
  pyramid.reload_templates = true
.. index::
   single: template system bindings
docs/narr/traversal.rst
@@ -456,6 +456,103 @@
   -specific request attributes are also available as described in
   :ref:`special_request_attributes`.
.. index::
   single: resource interfaces
.. _using_resource_interfaces:
Using Resource Interfaces In View Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Instead of registering your views with a ``context`` that names a Python
resource *class*, you can optionally register a view callable with a
``context`` which is an :term:`interface`.  An interface can be attached
arbitrarily to any resource object.  View lookup treats context interfaces
specially, and therefore the identity of a resource can be divorced from that
of the class which implements it.  As a result, associating a view with an
interface can provide more flexibility for sharing a single view between two
or more different implementations of a resource type.  For example, if two
resource objects of different Python class types share the same interface,
you can use the same view configuration to specify both of them as a
``context``.
In order to make use of interfaces in your application during view dispatch,
you must create an interface and mark up your resource classes or instances
with interface declarations that refer to this interface.
To attach an interface to a resource *class*, you define the interface and
use the :func:`zope.interface.implements` function to associate the interface
with the class.
.. code-block:: python
   :linenos:
   from zope.interface import Interface
   from zope.interface import implements
   class IHello(Interface):
       """ A marker interface """
   class Hello(object):
       implements(IHello)
To attach an interface to a resource *instance*, you define the interface and
use the :func:`zope.interface.alsoProvides` function to associate the
interface with the instance.  This function mutates the instance in such a
way that the interface is attached to it.
.. code-block:: python
   :linenos:
   from zope.interface import Interface
   from zope.interface import alsoProvides
   class IHello(Interface):
       """ A marker interface """
   class Hello(object):
       pass
   def make_hello():
       hello = Hello()
       alsoProvides(hello, IHello)
       return hello
Regardless of how you associate an interface, with a resource instance, or a
resource class, the resulting code to associate that interface with a view
callable is the same.  Assuming the above code that defines an ``IHello``
interface lives in the root of your application, and its module is named
"resources.py", the interface declaration below will associate the
``mypackage.views.hello_world`` view with resources that implement, or
provide, this interface.
.. code-block:: python
   :linenos:
   # config is an instance of pyramid.config.Configurator
   config.add_view('mypackage.views.hello_world', name='hello.html',
                   context='mypackage.resources.IHello')
Any time a resource that is determined to be the :term:`context` provides
this interface, and a view named ``hello.html`` is looked up against it as
per the URL, the ``mypackage.views.hello_world`` view callable will be
invoked.
Note, in cases where a view is registered against a resource class, and a
view is also registered against an interface that the resource class
implements, an ambiguity arises. Views registered for the resource class take
precedence over any views registered for any interface the resource class
implements. Thus, if one view configuration names a ``context`` of both the
class type of a resource, and another view configuration names a ``context``
of interface implemented by the resource's class, and both view
configurations are otherwise identical, the view registered for the context's
class will "win".
For more information about defining resources with interfaces for use within
view configuration, see :ref:`resources_which_implement_interfaces`.
References
----------
docs/narr/urldispatch.rst
@@ -6,28 +6,12 @@
URL Dispatch
============
:term:`URL dispatch` provides a simple way to map URLs to :term:`view`
code using a simple pattern matching language.  An ordered set of
patterns is checked one-by-one.  If one of the patterns matches the path
information associated with a request, a particular :term:`view
callable` is invoked.
:term:`URL dispatch` is one of two ways to perform :term:`resource
location` in :app:`Pyramid`; the other way is using :term:`traversal`.
If no route is matched using :term:`URL dispatch`, :app:`Pyramid` falls
back to :term:`traversal` to handle the :term:`request`.
It is the responsibility of the :term:`resource location` subsystem
(i.e., :term:`URL dispatch` or :term:`traversal`) to find the resource
object that is the :term:`context` of the :term:`request`. Once the
:term:`context` is determined, :term:`view lookup` is then responsible
for finding and invoking a :term:`view callable`.  A view callable is a
specific bit of code, defined in your application, that receives the
:term:`request` and returns a :term:`response` object.
Where appropriate, we will describe how view lookup interacts with
:term:`resource location`.  The :ref:`view_config_chapter` chapter describes
the details of :term:`view lookup`.
:term:`URL dispatch` provides a simple way to map URLs to :term:`view` code
using a simple pattern matching language.  An ordered set of patterns is
checked one-by-one.  If one of the patterns matches the path information
associated with a request, a particular :term:`view callable` is invoked.  A
view callable is a specific bit of code, defined in your application, that
receives the :term:`request` and returns a :term:`response` object.
High-Level Operational Overview
-------------------------------
@@ -37,18 +21,14 @@
matching patterns present in a *route map*.
If any route pattern matches the information in the :term:`request`,
:app:`Pyramid` will invoke :term:`view lookup` using a :term:`context`
resource generated by the route match.
:app:`Pyramid` will invoke :term:`view lookup` to find a matching view.
However, if no route pattern matches the information in the :term:`request`
provided to :app:`Pyramid`, it will fail over to using :term:`traversal` to
perform resource location and view lookup.
If no route pattern in the route map matches the information in the
:term:`request` provided in your application, :app:`Pyramid` will fail over
to using :term:`traversal` to perform resource location and view lookup.
Technically, URL dispatch is a :term:`resource location` mechanism (it finds
a context object).  But ironically, using URL dispatch (instead of
:term:`traversal`) allows you to avoid thinking about your application in
terms of "resources" entirely, because it allows you to directly map a
:term:`view callable` to a route.
.. index::
   single: route configuration
Route Configuration
-------------------
@@ -67,8 +47,8 @@
.. _config-add-route:
Configuring a Route via The ``add_route`` Configurator Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Configuring a Route to Match a View
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The :meth:`pyramid.config.Configurator.add_route` method adds a single
:term:`route configuration` to the :term:`application registry`.  Here's an
@@ -84,90 +64,45 @@
   config.add_route('myroute', '/prefix/{one}/{two}')
   config.add_view(myview, route_name='myroute')
.. versionchanged:: 1.0a4
    Prior to 1.0a4, routes allow for a marker starting with a ``:``, for
    example ``/prefix/:one/:two``.  This style is now deprecated
    in favor of ``{}`` usage which allows for additional functionality.
When a :term:`view callable` added to the configuration by way of
:meth:`~pyramid.config.Configurator.add_view` bcomes associated with a route
via its ``route_name`` predicate, that view callable will always be found and
invoked when the associated route pattern matches during a request.
.. index::
   single: route configuration; view callable
.. _add_route_view_config:
Route Configuration That Names a View Callable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. warning:: This section describes a feature which has been deprecated in
   Pyramid 1.1 and higher.  In order to reduce confusion and documentation
   burden, passing view-related parameters to
   :meth:`~pyramid.config.Configurator.add_route` is deprecated.
   In versions earlier than 1.1, a view was permitted to be connected to a
   route using a set of ``view*`` parameters passed to the
   :meth:`~pyramid.config.Configurator.add_route`.  This was a shorthand
   which replaced the need to perform a subsequent call to
   :meth:`~pyramid.config.Configurator.add_view` as described in
   :ref:`config-add-route`. For example, it was valid (and often recommended)
   to do:
   .. code-block:: python
      config.add_route('home', '/', view='mypackage.views.myview',
                        view_renderer='some/renderer.pt')
   Instead of the equivalent:
   .. code-block:: python
     config.add_route('home', '/')
     config.add_view('mypackage.views.myview', route_name='home')
                     renderer='some/renderer.pt')
   Passing ``view*`` arguments to ``add_route`` as shown in the first
   example above is now deprecated in favor of connecting a view to a
   predefined route via :meth:`~pyramid.config.Configurator.add_view` using
   the route's ``route_name`` parameter, as shown in the second example
   above.
   A deprecation warning is now issued when any view-related parameter is
   passed to ``Configurator.add_route``.  The recommended way to associate a
   view with a route is documented in :ref:`config-add-route`.
When a route configuration declaration names a ``view`` attribute, the value
of the attribute will reference a :term:`view callable`.  This view callable
will be invoked when the route matches.  A view callable, as described in
:ref:`views_chapter`, is developer-supplied code that "does stuff" as the
result of a request.
Here's an example route configuration that references a view callable:
More commonly, you will not use any ``add_view`` statements in your project's
"setup" code, instead only using ``add_route`` statements using a
:term:`scan` for to associate view callables with routes.  For example, if
this is a portion of your project's ``__init__.py``:
.. code-block:: python
   :linenos:
   # "config" below is presumed to be an instance of the
   # pyramid.config.Configurator class; "myview" is assumed
   # to be a "view callable" function
   from myproject.views import myview
   config.add_route('myroute', '/prefix/{one}/{two}', view=myview)
   # in your project's __init__.py (mypackage.__init__)
You can also pass a :term:`dotted Python name` as the ``view`` argument
rather than an actual callable:
   config.add_route('myroute', '/prefix/{one}/{two}')
   config.scan('mypackage')
Note that we don't call :meth:`~pyramid.config.Configurator.add_view` in this
setup code.  However, the above :term:`scan` execution
``config.scan('mypackage')`` will pick up all :term:`configuration
decoration`, including any objects decorated with the
:class:`pyramid.view.view_config` decorator in the ``mypackage`` Python
pakage.  For example, if you have a ``views.py`` in your package, a scan will
pick up any of its configuration decorators, so we can add one there that
that references ``myroute`` as a ``route_name`` parameter:
.. code-block:: python
   :linenos:
   # "config" below is presumed to be an instance of the
   # pyramid.config.Configurator class; "myview" is assumed
   # to be a "view callable" function
   config.add_route('myroute', '/prefix/{one}/{two}',
                    view='myproject.views.myview')
   # in your project's views.py module (mypackage.views)
When a route configuration names a ``view`` attribute, the :term:`view
callable` named as that ``view`` attribute will always be found and invoked
when the associated route pattern matches during a request.
   from pyramid.view import view_config
   from pyramid.response import Response
See :meth:`pyramid.config.Configurator.add_route` for a description of
view-related arguments.
   @view_config(route_name='myroute')
   def myview(request):
       return Response('OK')
THe above combination of ``add_route`` and ``scan`` is completely equivalent
to using the previous combination of ``add_route`` and ``add_view``.
.. index::
   single: route path pattern syntax
@@ -365,12 +300,11 @@
Route configuration declarations are evaluated in a specific order when a
request enters the system. As a result, the order of route configuration
declarations is very important.
The order that routes declarations are evaluated is the order in which they
are added to the application at startup time.  This is unlike
:term:`traversal`, which depends on emergent behavior which happens as a
result of traversing a resource tree.
declarations is very important.  The order that routes declarations are
evaluated is the order in which they are added to the application at startup
time.  (This is unlike a different way of mapping URLs to code that
:app:`Pyramid` provides, named :term:`traversal`, which does not depend on
pattern ordering).
For routes added via the :mod:`~pyramid.config.Configurator.add_route` method,
the order that routes are evaluated is the order in which they are added to
@@ -426,6 +360,9 @@
combine URL dispatch with :term:`traversal` as documented within
:ref:`hybrid_chapter`.
.. index::
   single: route configuration arguments
Route Configuration Arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -439,12 +376,16 @@
process.  Examples of route predicate arguments are ``pattern``, ``xhr``, and
``request_method``.
Other arguments are view configuration related arguments.  These only have an
effect when the route configuration names a ``view``. These arguments have
been deprecated as of :app:`Pyramid` 1.1 (see :ref:`add_route_view_config`).
Other arguments are ``name`` and ``factory``.  These arguments represent
neither predicates nor view configuration information.
.. warning:: Some arguments are view-configuration related arguments, such as
   ``view_renderer``.  These only have an effect when the route configuration
   names a ``view`` and these arguments have been deprecated as of
   :app:`Pyramid` 1.1.
.. index::
   single: route predicates (custom)
.. _custom_route_predicates:
@@ -578,32 +519,38 @@
See also :class:`pyramid.interfaces.IRoute` for more API documentation about
route objects.
.. index::
   single: route matching
Route Matching
--------------
The main purpose of route configuration is to match (or not match) the
``PATH_INFO`` present in the WSGI environment provided during a request
against a URL path pattern.
against a URL path pattern.  ``PATH_INFO`` represents the path portion of the
URL that was requested.
The way that :app:`Pyramid` does this is very simple.  When a request enters
the system, for each route configuration declaration present in the system,
:app:`Pyramid` checks the ``PATH_INFO`` against the pattern declared.
If any route matches, the route matching process stops.  The :term:`request`
is decorated with a special :term:`interface` which describes it as a "route
request", the :term:`context` resource is generated, and the context and the
resulting request are handed off to :term:`view lookup`.  During view lookup,
if a :term:`view callable` associated with the matched route is found, that
view is called.
:app:`Pyramid` checks the request's ``PATH_INFO`` against the pattern
declared.  This checking happens in the order that the routes were declared
via :meth:`pyramid.config.Configurator.add_route`.
When a route configuration is declared, it may contain :term:`route
predicate` arguments.  All route predicates associated with a route
declaration must be ``True`` for the route configuration to be used for a
given request.
given request during a check.  If any predicate in the set of :term:`route
predicate` arguments provided to a route configuration returns ``False``
during a check, that route is skipped and route matching continues through
the ordered set of routes.
If any predicate in the set of :term:`route predicate` arguments provided to
a route configuration returns ``False``, that route is skipped and route
matching continues through the ordered set of routes.
If any route matches, the route matching process stops and the :term:`view
lookup` subsystem takes over to find the most reasonable view callable for
the matched route.  Most often, there's only one view that will match (a view
configured with a ``route_name`` argument matching the matched route).  To
gain a better understanding of how routes and views are associated in a real
application, you can use the ``paster pviews`` command, as documented in
:ref:`displaying_matching_views`.
If no route matches after all route patterns are exhausted, :app:`Pyramid`
falls back to :term:`traversal` to do :term:`resource location` and
@@ -789,6 +736,9 @@
   single: matching the root URL
   single: root url (matching)
.. index::
   pair: matching; root URL
Matching the Root URL
---------------------
@@ -958,6 +908,9 @@
calling convention of ``(context, request)`` (``context`` will be the
exception object).
.. index::
   single: cleaning up after request
.. _cleaning_up_after_a_request:
Cleaning Up After a Request
@@ -1041,6 +994,9 @@
.. note:: See :ref:`security_chapter` for more information about
   :app:`Pyramid` security and ACLs.
.. index::
   pair: debugging; route matching
.. _debug_routematch_section:
Debugging Route Matching
@@ -1049,7 +1005,7 @@
It's useful to be able to take a peek under the hood when requests that enter
your application arent matching your routes as you expect them to.  To debug
route matching, use the ``PYRAMID_DEBUG_ROUTEMATCH`` environment variable or the
``debug_routematch`` configuration file setting (set either to ``true``).
``pyramid.debug_routematch`` configuration file setting (set either to ``true``).
Details of the route matching decision for a particular request to the
:app:`Pyramid` application will be printed to the ``stderr`` of the console
which you started the application from.  For example:
@@ -1072,45 +1028,12 @@
See :ref:`environment_chapter` for more information about how, and where to
set these values.
You can also use the ``paster proutes`` command to see a display of all the
routes configured in your application; for more information, see
:ref:`displaying_application_routes`.
.. index::
   pair: routes; printing
   single: paster proutes
.. _displaying_application_routes:
Displaying All Application Routes
---------------------------------
You can use the ``paster proutes`` command in a terminal window to print a
summary of routes related to your application.  Much like the ``paster
pshell`` command (see :ref:`interactive_shell`), the ``paster proutes``
command accepts two arguments.  The first argument to ``proutes`` is the path
to your application's ``.ini`` file.  The second is the ``app`` section name
inside the ``.ini`` file which points to your application.
For example:
.. code-block:: text
   :linenos:
   [chrism@thinko MyProject]$ ../bin/paster proutes development.ini MyProject
   Name            Pattern                        View
   ----            -------                        ----
   home            /                              <function my_view>
   home2           /                              <function my_view>
   another         /another                       None
   static/         static/*subpath                <static_view object>
   catchall        /*subpath                      <function static_view>
``paster proutes`` generates a table.  The table has three columns: a Name
name column, a Pattern column, and a View column.  The items listed in the
Name column are route names, the items listen in the Pattern column are route
patterns, and the items listed in the View column are representations of the
view callable that will be invoked when a request matches the associated
route pattern.  The view column may show ``None`` if no associated view
callable could be found.  If no routes are configured within your
application, nothing will be printed to the console when ``paster proutes``
is executed.
   pair: route; view callable lookup details
Route View Callable Registration and Lookup Details
---------------------------------------------------
@@ -1139,24 +1062,28 @@
  object is decorated with the route-specific interface.
- The fact that the request is decorated with a route-specific interface
  causes the view lookup machinery to always use the view callable registered
  using that interface by the route configuration to service requests that
  match the route pattern.
  causes the :term:`view lookup` machinery to always use the view callable
  registered using that interface by the route configuration to service
  requests that match the route pattern.
In this way, we supply a shortcut to the developer.  Under the hood, the
:term:`resource location` and :term:`view lookup` subsystems provided by
:app:`Pyramid` are still being utilized, but in a way which does not require
a developer to understand either of them in detail.  It also means that we
can allow a developer to combine :term:`URL dispatch` and :term:`traversal`
in various exceptional cases as documented in :ref:`hybrid_chapter`.
As we can see from the above description, technically, URL dispatch doesn't
actually map a URL pattern directly to a view callable.  Instead, URL
dispatch is a :term:`resource location` mechanism.  A :app:`Pyramid`
:term:`resource location` subsystem (i.e., :term:`URL dispatch` or
:term:`traversal`) finds a :term:`resource` object that is the
:term:`context` of a :term:`request`. Once the :term:`context` is determined,
a separate subsystem named :term:`view lookup` is then responsible for
finding and invoking a :term:`view callable` based on information available
in the context and the request.  When URL dispatch is used, the resource
location and view lookup subsystems provided by :app:`Pyramid` are still
being utilized, but in a way which does not require a developer to understand
either of them in detail.
To gain a better understanding of how routes and views are associated in a
real application, you can use the ``paster pviews`` command, as documented
in :ref:`displaying_matching_views`.
If no route is matched using :term:`URL dispatch`, :app:`Pyramid` falls back
to :term:`traversal` to handle the :term:`request`.
References
----------
A tutorial showing how :term:`URL dispatch` can be used to create a
:app:`Pyramid` application exists in :ref:`bfg_sql_wiki_tutorial`.
docs/narr/vhosting.rst
@@ -14,6 +14,9 @@
a URL "prefix", as well as serving a *portion* of a :term:`traversal`
based application under a root URL.
.. index::
   single: hosting an app under a prefix
Hosting an Application Under a URL Prefix
-----------------------------------------
docs/narr/viewconfig.rst
@@ -2,38 +2,27 @@
.. _view_configuration:
.. _view_lookup:
View Configuration
==================
.. index::
   single: view lookup
:term:`View configuration` controls how :term:`view lookup` operates in
your application. In earlier chapters, you have been exposed to a few
simple view configuration declarations without much explanation. In this
chapter we will explore the subject in detail.
.. _view_lookup:
View Lookup and Invocation
--------------------------
:term:`View lookup` is the :app:`Pyramid` subsystem responsible for finding
an invoking a :term:`view callable`.  The view lookup subsystem is passed a
:term:`context` and a :term:`request` object.
and invoking a :term:`view callable`.  :term:`View configuration` controls how
:term:`view lookup` operates in your application.  During any given request,
view configuration information is compared against request data by the view
lookup subsystem in order to find the "best" view callable for that request.
:term:`View configuration` information stored within in the
:term:`application registry` is compared against the context and request by
the view lookup subsystem in order to find the "best" view callable for the
set of circumstances implied by the context and request.
In earlier chapters, you have been exposed to a few simple view configuration
declarations without much explanation. In this chapter we will explore the
subject in detail.
:term:`View predicate` attributes are an important part of view
configuration that enables the :term:`View lookup` subsystem to find and
invoke the appropriate view.  Predicate attributes can be thought of
like "narrowers".  In general, the greater number of predicate
attributes possessed by a view's configuration, the more specific the
circumstances need to be before the registered view callable will be
invoked.
.. index::
   pair: resource; mapping to view callable
   pair: URL pattern; mapping to view callable
Mapping a Resource or URL Pattern to a View Callable
----------------------------------------------------
@@ -47,7 +36,7 @@
A view configuration statement is made about information present in the
:term:`context` resource and the :term:`request`.
View configuration is performed in one of these ways:
View configuration is performed in one of two ways:
- by running a :term:`scan` against application source code which has a
  :class:`pyramid.view.view_config` decorator attached to a Python object as
@@ -56,16 +45,8 @@
- by using the :meth:`pyramid.config.Configurator.add_view` method as per
  :ref:`mapping_views_using_imperative_config_section`.
- By specifying a view within a :term:`route configuration`.  View
  configuration via a route configuration is performed by using the
  :meth:`pyramid.config.Configurator.add_route` method, passing a ``view``
  argument specifying a view callable. This pattern of view configuration is
  deprecated as of :app:`Pyramid` 1.1.
.. note:: A package named ``pyramid_handlers`` (available from PyPI) provides
   an analogue of :term:`Pylons` -style "controllers", which are a special
   kind of view class which provides more automation when your application
   uses :term:`URL dispatch` solely.
.. index::
   single: view configuration parameters
.. _view_configuration_parameters:
@@ -79,12 +60,15 @@
to narrow the set of circumstances in which :term:`view lookup` will find a
particular view callable.
In general, the fewer number of predicates which are supplied to a
particular view configuration, the more likely it is that the associated
view callable will be invoked.  The greater the number supplied, the
less likely.  A view with five predicates will always be found and
evaluated before a view with two, for example.  All predicates must
match for the associated view to be called.
:term:`View predicate` attributes are an important part of view configuration
that enables the :term:`view lookup` subsystem to find and invoke the
appropriate view.  The greater number of predicate attributes possessed by a
view's configuration, the more specific the circumstances need to be before
the registered view callable will be invoked.  The fewer number of predicates
which are supplied to a particular view configuration, the more likely it is
that the associated view callable will be invoked.  A view with five
predicates will always be found and evaluated before a view with two, for
example.  All predicates must match for the associated view to be called.
This does not mean however, that :app:`Pyramid` "stops looking" when it
finds a view registration with predicates that don't match.  If one set
@@ -99,11 +83,13 @@
representing a "not found" (404) page.  See :ref:`changing_the_notfound_view`
for more information about changing the default notfound view.
Some view configuration arguments are non-predicate arguments.  These tend to
modify the response of the view callable or prevent the view callable from
Other view configuration arguments are non-predicate arguments.  These tend
to modify the response of the view callable or prevent the view callable from
being invoked due to an authorization policy.  The presence of non-predicate
arguments in a view configuration does not narrow the circumstances in which
the view callable will be invoked.
.. _nonpredicate_view_args:
Non-Predicate Arguments
+++++++++++++++++++++++
@@ -249,8 +235,10 @@
configured view.
``name``
  The :term:`view name` required to match this view callable.  Read
  :ref:`traversal_chapter` to understand the concept of a view name.
  The :term:`view name` required to match this view callable.  A ``name``
  argument is typically only used when your application uses
  :term:`traversal`. Read :ref:`traversal_chapter` to understand the concept
  of a view name.
  If ``name`` is not supplied, the empty string is used (implying the default
  view).
@@ -405,29 +393,23 @@
.. _mapping_views_using_a_decorator_section:
View Configuration Using the ``@view_config`` Decorator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For better locality of reference, you may use the
:class:`pyramid.view.view_config` decorator to associate your view functions
with URLs instead of using imperative configuration for the same purpose.
Adding View Configuration Using the ``@view_config`` Decorator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. warning::
   Using this feature tends to slows down application startup slightly, as
   more work is performed at application startup to scan for view
   declarations.
   configuration declarations.  For maximum startup performance, use the view
   configuration method described in
   :ref:`mapping_views_using_imperative_config_section` instead.
Usage of the ``view_config`` decorator is a form of :term:`declarative
configuration` in decorator form.  :class:`~pyramid.view.view_config` can be
used to associate :term:`view configuration` information -- as done via the
equivalent imperative code -- with a function that acts as a :app:`Pyramid`
view callable.  All arguments to the
:meth:`pyramid.config.Configurator.add_view` method (save for the ``view``
argument) are available in decorator form and mean precisely the same thing.
The :class:`~pyramid.view.view_config` decorator can be used to associate
:term:`view configuration` information with a function, method, or class that
acts as a :app:`Pyramid` view callable.
An example of the :class:`~pyramid.view.view_config` decorator might reside in
a :app:`Pyramid` application module ``views.py``:
Here's an example of the :class:`~pyramid.view.view_config` decorator that
lives within a :app:`Pyramid` application module ``views.py``:
.. ignore-next-block
.. code-block:: python
@@ -437,8 +419,7 @@
   from pyramid.view import view_config
   from pyramid.response import Response
   @view_config(name='my_view', request_method='POST', context=MyResource,
                permission='read')
   @view_config(route_name='ok', request_method='POST', permission='read')
   def my_view(request):
       return Response('OK')
@@ -449,9 +430,8 @@
.. code-block:: python
   :linenos:
   config.add_view('mypackage.views.my_view', name='my_view',
                   request_method='POST', context=MyResource,
                   permission='read')
   config.add_view('mypackage.views.my_view', route_name='ok',
                   request_method='POST', permission='read')
All arguments to ``view_config`` may be omitted.  For example:
@@ -494,6 +474,17 @@
allows you to supply a ``package`` argument to better control exactly *which*
code will be scanned.
All arguments to the :class:`~pyramid.view.view_config` decorator mean
precisely the same thing as they would if they were passed as arguments to
the :meth:`pyramid.config.Configurator.add_view` method save for the ``view``
argument.  Usage of the :class:`~pyramid.view.view_config` decorator is a
form of :term:`declarative configuration`, while
:meth:`pyramid.config.Configurator.add_view` is a form of :term:`imperative
configuration`.  However, they both do the same thing.
.. index::
   single: view_config placement
``@view_config`` Placement
++++++++++++++++++++++++++
@@ -508,7 +499,7 @@
   from pyramid.view import view_config
   from pyramid.response import Response
   @view_config(name='edit')
   @view_config(route_name='edit')
   def edit(request):
       return Response('edited!')
@@ -523,7 +514,7 @@
   from pyramid.response import Response
   from pyramid.view import view_config
   @view_config()
   @view_config(route_name='hello')
   class MyView(object):
       def __init__(self, request):
           self.request = request
@@ -548,7 +539,7 @@
       def __call__(self):
           return Response('hello')
   my_view = view_config()(MyView)
   my_view = view_config(route_name='hello')(MyView)
More than one :class:`~pyramid.view.view_config` decorator can be stacked on
top of any number of others.  Each decorator creates a separate view
@@ -560,8 +551,8 @@
   from pyramid.view import view_config
   from pyramid.response import Response
   @view_config(name='edit')
   @view_config(name='change')
   @view_config(route_name='edit')
   @view_config(route_name='change')
   def edit(request):
       return Response('edited!')
@@ -579,7 +570,7 @@
       def __init__(self, request):
           self.request = request
       @view_config(name='hello')
       @view_config(route_name='hello')
       def amethod(self):
           return Response('hello')
@@ -601,7 +592,7 @@
   from pyramid.response import Response
   from pyramid.view import view_config
   @view_config(attr='amethod', name='hello')
   @view_config(attr='amethod', route_name='hello')
   class MyView(object):
       def __init__(self, request):
           self.request = request
@@ -614,13 +605,14 @@
.. _mapping_views_using_imperative_config_section:
View Registration Using :meth:`~pyramid.config.Configurator.add_view`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Adding View Configuration Using :meth:`~pyramid.config.Configurator.add_view`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The :meth:`pyramid.config.Configurator.add_view` method within
:ref:`configuration_module` is used to configure a view imperatively.  The
arguments to this method are very similar to the arguments that you provide
to the ``@view_config`` decorator.  For example:
:ref:`configuration_module` is used to configure a view "imperatively"
(without a :class:`~pyramid.view.view_config` decorator).  The arguments to
this method are very similar to the arguments that you provide to the
:class:`~pyramid.view.view_config` decorator.  For example:
.. code-block:: python
   :linenos:
@@ -632,108 +624,17 @@
   # config is assumed to be an instance of the
   # pyramid.config.Configurator class
   config.add_view(hello_world, name='hello.html')
   config.add_view(hello_world, route_name='hello')
The first argument, ``view``, is required.  It must either be a Python object
which is the view itself or a :term:`dotted Python name` to such an object.
All other arguments are optional.  See
:meth:`pyramid.config.Configurator.add_view` for more information.
In the above example, ``view`` is the ``hello_world`` function.  All other
arguments are optional.  See :meth:`pyramid.config.Configurator.add_view` for
more information.
.. index::
   single: resource interfaces
.. _using_resource_interfaces:
Using Resource Interfaces In View Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Instead of registering your views with a ``context`` that names a Python
resource *class*, you can optionally register a view callable with a
``context`` which is an :term:`interface`.  An interface can be attached
arbitrarily to any resource object.  View lookup treats context interfaces
specially, and therefore the identity of a resource can be divorced from that
of the class which implements it.  As a result, associating a view with an
interface can provide more flexibility for sharing a single view between two
or more different implementations of a resource type.  For example, if two
resource objects of different Python class types share the same interface,
you can use the same view configuration to specify both of them as a
``context``.
In order to make use of interfaces in your application during view dispatch,
you must create an interface and mark up your resource classes or instances
with interface declarations that refer to this interface.
To attach an interface to a resource *class*, you define the interface and
use the :func:`zope.interface.implements` function to associate the interface
with the class.
.. code-block:: python
   :linenos:
   from zope.interface import Interface
   from zope.interface import implements
   class IHello(Interface):
       """ A marker interface """
   class Hello(object):
       implements(IHello)
To attach an interface to a resource *instance*, you define the interface and
use the :func:`zope.interface.alsoProvides` function to associate the
interface with the instance.  This function mutates the instance in such a
way that the interface is attached to it.
.. code-block:: python
   :linenos:
   from zope.interface import Interface
   from zope.interface import alsoProvides
   class IHello(Interface):
       """ A marker interface """
   class Hello(object):
       pass
   def make_hello():
       hello = Hello()
       alsoProvides(hello, IHello)
       return hello
Regardless of how you associate an interface, with a resource instance, or a
resource class, the resulting code to associate that interface with a view
callable is the same.  Assuming the above code that defines an ``IHello``
interface lives in the root of your application, and its module is named
"resources.py", the interface declaration below will associate the
``mypackage.views.hello_world`` view with resources that implement, or
provide, this interface.
.. code-block:: python
   :linenos:
   # config is an instance of pyramid.config.Configurator
   config.add_view('mypackage.views.hello_world', name='hello.html',
                   context='mypackage.resources.IHello')
Any time a resource that is determined to be the :term:`context` provides
this interface, and a view named ``hello.html`` is looked up against it as
per the URL, the ``mypackage.views.hello_world`` view callable will be
invoked.
Note, in cases where a view is registered against a resource class, and a
view is also registered against an interface that the resource class
implements, an ambiguity arises. Views registered for the resource class take
precedence over any views registered for any interface the resource class
implements. Thus, if one view configuration names a ``context`` of both the
class type of a resource, and another view configuration names a ``context``
of interface implemented by the resource's class, and both view
configurations are otherwise identical, the view registered for the context's
class will "win".
For more information about defining resources with interfaces for use within
view configuration, see :ref:`resources_which_implement_interfaces`.
When you use only :meth:`~pyramid.config.Configurator.add_view` to add view
configurations, you don't need to issue a :term:`scan` in order for the view
configuration to take effect.
.. index::
   single: view security
@@ -756,8 +657,9 @@
   # config is an instance of pyramid.config.Configurator
   config.add_view('myproject.views.add_entry', name='add.html',
                   context='myproject.resources.IBlog', permission='add')
   config.add_route('add', '/add.html', factory='mypackage.Blog')
   config.add_view('myproject.views.add_entry', route_name='add',
                   permission='add')
When an :term:`authorization policy` is enabled, this view will be protected
with the ``add`` permission.  The view will *not be called* if the user does
@@ -777,105 +679,71 @@
It's useful to be able to debug :exc:`NotFound` error responses when they
occur unexpectedly due to an application registry misconfiguration.  To debug
these errors, use the ``PYRAMID_DEBUG_NOTFOUND`` environment variable or the
``debug_notfound`` configuration file setting.  Details of why a view was not
found will be printed to ``stderr``, and the browser representation of the
error will include the same information.  See :ref:`environment_chapter` for
more information about how, and where to set these values.
``pyramid.debug_notfound`` configuration file setting.  Details of why a view
was not found will be printed to ``stderr``, and the browser representation of
the error will include the same information.  See :ref:`environment_chapter`
for more information about how, and where to set these values.
.. index::
   pair: matching views; printing
   single: paster pviews
   single: HTTP caching
.. _displaying_matching_views:
.. _influencing_http_caching:
Displaying Matching Views for a Given URL
-----------------------------------------
Influencing HTTP Caching
------------------------
For a big application with several views, it can be hard to keep the view
configuration details in your head, even if you defined all the views
yourself. You can use the ``paster pviews`` command in a terminal window to
print a summary of matching routes and views for a given URL in your
application. The ``paster pviews`` command accepts three arguments. The
first argument to ``pviews`` is the path to your application's ``.ini`` file.
The second is the ``app`` section name inside the ``.ini`` file which points
to your application. The third is the URL to test for matching views.
.. note:: This feature is new in Pyramid 1.1.
Here is an example for a simple view configuration using :term:`traversal`:
When a non-``None`` ``http_cache`` argument is passed to a view
configuration, Pyramid will set ``Expires`` and ``Cache-Control`` response
headers in the resulting response, causing browsers to cache the response
data for some time.  See ``http_cache`` in :ref:`nonpredicate_view_args` for
the its allowable values and what they mean.
.. code-block:: text
   :linenos:
Sometimes it's undesirable to have these headers set as the result of
returning a response from a view, even though you'd like to decorate the view
with a view configuration decorator that has ``http_cache``.  Perhaps there's
an alternate branch in your view code that returns a response that should
never be cacheable, while the "normal" branch returns something that should
always be cacheable.  If this is the case, set the ``prevent_auto`` attribute
of the ``response.cache_control`` object to a non-``False`` value.  For
example, the below view callable is configured with a ``@view_config``
decorator that indicates any response from the view should be cached for 3600
seconds.  However, the view itself prevents caching from taking place unless
there's a ``should_cache`` GET or POST variable:
   $ ../bin/paster pviews development.ini tutorial /FrontPage
.. code-block:: python
   URL = /FrontPage
   from pyramid.view import view_config
       context: <tutorial.models.Page object at 0xa12536c>
       view name:
   @view_config(http_cache=3600)
   def view(request):
       response = Response()
       if not 'should_cache' in request.params:
           response.cache_control.prevent_auto = True
       return response
       View:
       -----
       tutorial.views.view_page
       required permission = view
Note that the ``http_cache`` machinery will overwrite or add to caching
headers you set within the view itself unless you use ``preserve_auto``.
The output always has the requested URL at the top and below that all the
views that matched with their view configuration details. In this example
only one view matches, so there is just a single *View* section. For each
matching view, the full code path to the associated view callable is shown,
along with any permissions and predicates that are part of that view
configuration.
You can also turn of the effect of ``http_cache`` entirely for the duration
of a Pyramid application lifetime.  To do so, set the
``PYRAMID_PREVENT_HTTP_CACHE`` environment variable or the
``pyramid.prevent_http_cache`` configuration value setting to a true value.
For more information, see :ref:`preventing_http_caching`.
A more complex configuration might generate something like this:
Note that setting ``pyramid.prevent_http_cache`` will have no effect on caching
headers that your application code itself sets.  It will only prevent caching
headers that would have been set by the Pyramid HTTP caching machinery
invoked as the result of the ``http_cache`` argument to view configuration.
.. code-block:: text
   :linenos:
.. index::
   pair: view configuration; debugging
   $ ../bin/paster pviews development.ini shootout /about
Debugging View Configuration
----------------------------
   URL = /about
       context: <shootout.models.RootFactory object at 0xa56668c>
       view name: about
       Route:
       ------
       route name: about
       route pattern: /about
       route path: /about
       subpath:
       route predicates (request method = GET)
           View:
           -----
           shootout.views.about_view
           required permission = view
           view predicates (request_param testing, header X/header)
       Route:
       ------
       route name: about_post
       route pattern: /about
       route path: /about
       subpath:
       route predicates (request method = POST)
           View:
           -----
           shootout.views.about_view_post
           required permission = view
           view predicates (request_param test)
           View:
           -----
           shootout.views.about_view_post2
           required permission = view
           view predicates (request_param test2)
In this case, we are dealing with a :term:`URL dispatch` application. This
specific URL has two matching routes. The matching route information is
displayed first, followed by any views that are associated with that route.
As you can see from the second matching route output, a route can be
associated with more than one view.
For a URL that doesn't match any views, ``paster pviews`` will simply print
out a *Not found* message.
See :ref:`displaying_matching_views` for information about how to display
each of the view callables that might match for a given URL.  This can be an
effective way to figure out why a particular view callable is being called
instead of the one you'd like to be called.
docs/narr/views.rst
@@ -3,10 +3,10 @@
Views
=====
One of the primary jobs of :app:`Pyramid` is to find and invoke a
:term:`view callable` when a :term:`request` reaches your application.  View
callables are bits of code which do something interesting in response to a
request made to your application.
One of the primary jobs of :app:`Pyramid` is to find and invoke a :term:`view
callable` when a :term:`request` reaches your application.  View callables
are bits of code which do something interesting in response to a request made
to your application.  They are the "meat" of any interesting web application.
.. note:: 
@@ -17,48 +17,32 @@
   that implements a view *callable*, and the process of view
   *lookup*.
The :ref:`urldispatch_chapter`, and :ref:`traversal_chapter` chapters
describes how, using information from the :term:`request`, a
:term:`context` resource is computed.  But the context resource itself
isn't very useful without an associated :term:`view callable`.  A view
callable returns a response to a user, often using the context resource
to do so.
This chapter describes how view callables should be defined. We'll have to
wait until a following chapter (entitled :ref:`view_config_chapter`) to find
out how we actually tell :app:`Pyramid` to wire up view callables to
particular URL patterns and other request circumstances.
The job of actually locating and invoking the "best" :term:`view callable` is
the job of the :term:`view lookup` subsystem.  The view lookup subsystem
compares the resource supplied by :term:`resource location` and information
in the :term:`request` against :term:`view configuration` statements made by
the developer to choose the most appropriate view callable for a specific
set of circumstances.
This chapter describes how view callables work. In the
:ref:`view_config_chapter` chapter, there are details about performing
view configuration, and a detailed explanation of view lookup.
.. index::
   single: view callables
View Callables
--------------
View callables are, at the risk of sounding obvious, callable Python
objects. Specifically, view callables can be functions, classes, or
instances that implement an ``__call__`` method (making the
instance callable).
objects. Specifically, view callables can be functions, classes, or instances
that implement an ``__call__`` method (making the instance callable).
View callables must, at a minimum, accept a single argument named
``request``.  This argument represents a :app:`Pyramid` :term:`Request`
object.  A request object encapsulates a WSGI environment provided to
:app:`Pyramid` by the upstream :term:`WSGI` server. As you might expect,
the request object contains everything your application needs to know
about the specific HTTP request being made.
object.  A request object represents a :term:`WSGI` environment provided to
:app:`Pyramid` by the upstream WSGI server. As you might expect, the request
object contains everything your application needs to know about the specific
HTTP request being made.
A view callable's ultimate responsibility is to create a :mod:`Pyramid`
:term:`Response` object. This can be done by creating the response object in
the view callable code and returning it directly, as we will be doing in this
chapter. However, if a view callable does not return a response itself, it
can be configured to use a :term:`renderer` that converts its return value
into a :term:`Response` object. Using renderers is the common way that
templates are used with view callables to generate markup: see the
:ref:`renderers_chapter` chapter for details.  In some cases, a response may
also be generated by raising an exception within a view callable.
:term:`Response` object. This can be done by creating a :term:`Response`
object in the view callable code and returning it directly or by raising
special kinds of exceptions from within the body of a view callable.
.. index::
   single: view calling convention
@@ -129,85 +113,6 @@
method of the class if you'd like the class to represent a collection of 
related view callables.
.. note:: A package named :term:`pyramid_handlers` (available from PyPI)
   provides an analogue of :term:`Pylons` -style "controllers", which are a
   special kind of view class which provides more automation when your
   application uses :term:`URL dispatch` solely.
.. index::
   single: view calling convention
.. _request_and_context_view_definitions:
Alternate View Callable Argument/Calling Conventions
----------------------------------------------------
Usually, view callables are defined to accept only a single argument:
``request``.  However, view callables may alternately be defined as classes,
functions, or any callable that accept *two* positional arguments: a
:term:`context` resource as the first argument and a :term:`request` as the
second argument.
The :term:`context` and :term:`request` arguments passed to a view function
defined in this style can be defined as follows:
context
  The :term:`resource` object found via tree :term:`traversal` or :term:`URL
  dispatch`.
request
  A :app:`Pyramid` Request object representing the current WSGI request.
The following types work as view callables in this style:
#. Functions that accept two arguments: ``context``, and ``request``,
   e.g.:
   .. code-block:: python
      :linenos:
      from pyramid.response import Response
      def view(context, request):
          return Response('OK')
#. Classes that have an ``__init__`` method that accepts ``context,
   request`` and a ``__call__`` method which accepts no arguments, e.g.:
   .. code-block:: python
      :linenos:
      from pyramid.response import Response
      class view(object):
          def __init__(self, context, request):
              self.context = context
              self.request = request
          def __call__(self):
              return Response('OK')
#. Arbitrary callables that have a ``__call__`` method that accepts
   ``context, request``, e.g.:
   .. code-block:: python
      :linenos:
      from pyramid.response import Response
      class View(object):
          def __call__(self, context, request):
              return Response('OK')
      view = View() # this is the view callable
This style of calling convention is most useful for :term:`traversal` based
applications, where the context object is frequently used within the view
callable code itself.
No matter which view calling convention is used, the view code always has
access to the context via ``request.context``.
.. index::
   single: view response
   single: response
@@ -234,15 +139,16 @@
inherit from :class:`pyramid.response.Response`.  For example, an instance of
the class :class:`pyramid.httpexceptions.HTTPFound` is also a valid response
object because it inherits from :class:`~pyramid.response.Response`.  For
examples, see :ref:`http_exceptions` and ref:`http_redirect`.
examples, see :ref:`http_exceptions` and :ref:`http_redirect`.
You can also return objects from view callables that aren't instances of (or
instances of classes which are subclasses of)
:class:`pyramid.response.Response` in various circumstances.  This can be
helpful when writing tests and when attempting to share code between view
callables.  See :ref:`renderers_chapter` for the common way to allow for
this.  A much less common way to allow for view callables to return
non-Response objects is documented in :ref:`using_iresponse`.
.. note::
   You can also return objects from view callables that aren't instances of
   :class:`pyramid.response.Response` in various circumstances.  This can be
   helpful when writing tests and when attempting to share code between view
   callables.  See :ref:`renderers_chapter` for the common way to allow for
   this.  A much less common way to allow for view callables to return
   non-Response objects is documented in :ref:`using_iresponse`.
.. index::
   single: view exceptions
@@ -483,7 +389,7 @@
always part of a :term:`view`.  For a general overview of how to handle form
submission data using the :term:`WebOb` API, see :ref:`webob_chapter` and
`"Query and POST variables" within the WebOb documentation
<http://pythonpaste.org/webob/reference.html#query-post-variables>`_.
<http://docs.webob.org/en/latest/reference.html#query-post-variables>`_.
:app:`Pyramid` defers to WebOb for its request and response implementations,
and handling form submission data is a property of the request
implementation.  Understanding WebOb's request API is the key to
@@ -591,3 +497,88 @@
   configuration.  The keys are still (byte) strings.
.. index::
   single: view calling convention
.. _request_and_context_view_definitions:
Alternate View Callable Argument/Calling Conventions
----------------------------------------------------
Usually, view callables are defined to accept only a single argument:
``request``.  However, view callables may alternately be defined as classes,
functions, or any callable that accept *two* positional arguments: a
:term:`context` resource as the first argument and a :term:`request` as the
second argument.
The :term:`context` and :term:`request` arguments passed to a view function
defined in this style can be defined as follows:
context
  The :term:`resource` object found via tree :term:`traversal` or :term:`URL
  dispatch`.
request
  A :app:`Pyramid` Request object representing the current WSGI request.
The following types work as view callables in this style:
#. Functions that accept two arguments: ``context``, and ``request``,
   e.g.:
   .. code-block:: python
      :linenos:
      from pyramid.response import Response
      def view(context, request):
          return Response('OK')
#. Classes that have an ``__init__`` method that accepts ``context,
   request`` and a ``__call__`` method which accepts no arguments, e.g.:
   .. code-block:: python
      :linenos:
      from pyramid.response import Response
      class view(object):
          def __init__(self, context, request):
              self.context = context
              self.request = request
          def __call__(self):
              return Response('OK')
#. Arbitrary callables that have a ``__call__`` method that accepts
   ``context, request``, e.g.:
   .. code-block:: python
      :linenos:
      from pyramid.response import Response
      class View(object):
          def __call__(self, context, request):
              return Response('OK')
      view = View() # this is the view callable
This style of calling convention is most useful for :term:`traversal` based
applications, where the context object is frequently used within the view
callable code itself.
No matter which view calling convention is used, the view code always has
access to the context via ``request.context``.
.. index::
   single: Pylons-style controller dispatch
Pylons-1.0-Style "Controller" Dispatch
--------------------------------------
A package named :term:`pyramid_handlers` (available from PyPI) provides an
analogue of :term:`Pylons` -style "controllers", which are a special kind of
view class which provides more automation when your application uses
:term:`URL dispatch` solely.
docs/narr/webob.rst
@@ -22,9 +22,9 @@
WebOb is a project separate from :app:`Pyramid` with a separate set of
authors and a fully separate `set of documentation
<http://pythonpaste.org/webob/>`_.  Pyramid adds some functionality to the
standard WebOb request, which is documented in the :ref:`request_module` API
documentation.
<http://docs.webob.org/en/latest/index.html>`_.  Pyramid adds some
functionality to the standard WebOb request, which is documented in the
: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
@@ -35,7 +35,7 @@
WSGI requests and responses; however, we won't cover that use case in this
document, as users of :app:`Pyramid` don't typically need to use the
WSGI-related features of WebOb directly.  The `reference documentation
<http://pythonpaste.org/webob/reference.html>`_ shows many examples of
<http://docs.webob.org/en/latest/reference.html>`_ shows many examples of
creating requests and using response objects in this manner, however.
.. index::
@@ -77,6 +77,10 @@
    ``POST`` that is *not* a form submission, or a request like a
    ``PUT``.  You can also get ``req.body_file`` for a file-like
    object.
``req.json_body``
    The JSON-decoded contents of the body of the request. See
    :ref:`request_json_body`.
``req.cookies``:
    A simple dictionary of all the cookies.
@@ -239,6 +243,73 @@
API documentation for a multidict exists as
:class:`pyramid.interfaces.IMultiDict`.
.. index::
   pair: json_body; request
.. _request_json_body:
Dealing With A JSON-Encoded Request Body
++++++++++++++++++++++++++++++++++++++++
.. note:: this feature is new as of Pyramid 1.1.
:attr:`pyramid.request.Request.json_body` is a property that returns a
:term:`JSON` -decoded representation of the request body.  If the request
does not have a body, or the body is not a properly JSON-encoded value, an
exception will be raised when this attribute is accessed.
This attribute is useful when you invoke a Pyramid view callable via
e.g. jQuery's ``$.ajax`` function, which has the potential to send a request
with a JSON-encoded body.
Using ``request.json_body`` is equivalent to:
.. code-block:: python
   from json import loads
   loads(request.body, encoding=request.charset)
Here's how to construct an AJAX request in Javascript using :term:`jQuery`
that allows you to use the ``request.json_body`` attribute when the request
is sent to a Pyramid application:
.. code-block:: javascript
    jQuery.ajax({type:'POST',
                 url: 'http://localhost:6543/', // the pyramid server
                 data: JSON.stringify({'a':1}),
                 contentType: 'application/json; charset=utf-8'});
When such a request reaches a view in your application, the
``request.json_body`` attribute will be available in the view callable body.
.. code-block:: javascript
    @view_config(renderer='string')
    def aview(request):
        print request.json_body
        return 'OK'
For the above view, printed to the console will be:
.. code-block:: python
    {u'a': 1}
For bonus points, here's a bit of client-side code that will produce a
request that has a body suitable for reading via ``request.json_body`` using
Python's ``urllib2`` instead of a Javascript AJAX request:
.. code-block:: python
    import urllib2
    import json
    json_payload = json.dumps({'a':1})
    headers = {'Content-Type':'application/json; charset=utf-8'}
    req = urllib2.Request('http://localhost:6543/', json_payload, headers)
    resp = urllib2.urlopen(req)
More Details
++++++++++++
@@ -246,8 +317,8 @@
- The :class:`pyramid.request.Request` API documentation.
- The `WebOb documentation <http://pythonpaste.org/webob>`_.  All
  methods and attributes of a ``webob.Request`` documented within the
- The `WebOb documentation <http://docs.webob.org/en/latest/index.html>`_.
  All methods and attributes of a ``webob.Request`` documented within the
  WebOb documentation will work with request objects created by
  :app:`Pyramid`.
@@ -331,7 +402,7 @@
``response.last_modified = os.path.getmtime(filename)``.
The details are available in the `extracted Response documentation
<http://pythonpaste.org/webob/class-webob.Response.html>`_.
<http://docs.webob.org/en/latest/modules/webob.html#headers>`_.
.. index::
   single: response (creating)
@@ -354,7 +425,7 @@
``default_content_type`` you can override this behavior.
.. index::
   single: response exceptions
   single: exception responses
Exception Responses
+++++++++++++++++++
@@ -390,5 +461,6 @@
More details about the response object API are available in the
:mod:`pyramid.response` documentation.  More details about exception
responses are in the :mod:`pyramid.httpexceptions` API documentation.  The
`WebOb documentation <http://pythonpaste.org/webob>`_ is also useful.
`WebOb documentation <http://docs.webob.org/en/latest/index.html>`_ is also
useful.
docs/narr/zca.rst
@@ -245,7 +245,6 @@
       globalreg = getGlobalSiteManager()
       config = Configurator(registry=globalreg)
       config.setup_registry(settings=settings)
       config.hook_zca()
       config.include('some.other.application')
       return config.make_wsgi_app()
docs/remake
New file
@@ -0,0 +1 @@
make clean html SPHINXBUILD=../env26/bin/sphinx-build
docs/tutorials/modwsgi/index.rst
@@ -102,8 +102,7 @@
       # play badly with C extensions.
       WSGIApplicationGroup %{GLOBAL}
       WSGIPassAuthorization On
       WSGIDaemonProcess pyramid user=chrism group=staff processes=1 \
          threads=4 \
       WSGIDaemonProcess pyramid user=chrism group=staff threads=4 \
          python-path=/Users/chrism/modwsgi/env/lib/python2.6/site-packages
       WSGIScriptAlias /myapp /Users/chrism/modwsgi/env/pyramid.wsgi
docs/tutorials/wiki/src/authorization/development.ini
@@ -1,24 +1,21 @@
[app:tutorial]
use = egg:tutorial
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
pyramid.include = pyramid_debugtoolbar
                  pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[pipeline:main]
pipeline =
    egg:WebError#evalerror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    tutorial
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[server:main]
use = egg:Paste#http
docs/tutorials/wiki/src/authorization/production.ini
@@ -1,11 +1,14 @@
[app:tutorial]
use = egg:tutorial
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
pyramid.include = pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[filter:weberror]
@@ -22,16 +25,10 @@
;smtp_use_tls =
;error_message =
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[pipeline:main]
pipeline =
    weberror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    tutorial
[server:main]
docs/tutorials/wiki/src/authorization/setup.py
@@ -9,8 +9,8 @@
requires = [
    'pyramid',
    'repoze.zodbconn',
    'repoze.tm2>=1.0b1', # default_commit_veto
    'repoze.retry',
    'pyramid_tm',
    'pyramid_debugtoolbar',
    'ZODB3',
    'WebError',
    'docutils',
docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,7 +31,7 @@
body h3,
body h4,
body h5,
body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
@@ -6,8 +6,9 @@
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -31,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -43,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
docs/tutorials/wiki/src/basiclayout/development.ini
@@ -1,24 +1,21 @@
[app:tutorial]
use = egg:tutorial
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
pyramid.include = pyramid_debugtoolbar
                  pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[pipeline:main]
pipeline =
    egg:WebError#evalerror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    tutorial
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[server:main]
use = egg:Paste#http
docs/tutorials/wiki/src/basiclayout/production.ini
@@ -1,11 +1,14 @@
[app:tutorial]
use = egg:tutorial
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
pyramid.include = pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[filter:weberror]
@@ -22,16 +25,10 @@
;smtp_use_tls =
;error_message =
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[pipeline:main]
pipeline =
    weberror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    tutorial
[server:main]
docs/tutorials/wiki/src/basiclayout/setup.py
@@ -9,8 +9,8 @@
requires = [
    'pyramid',
    'repoze.zodbconn',
    'repoze.tm2>=1.0b1', # default_commit_veto
    'repoze.retry',
    'pyramid_tm',
    'pyramid_debugtoolbar',
    'ZODB3',
    'WebError',
    ]
docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,7 +31,7 @@
body h3,
body h4,
body h5,
body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
@@ -6,8 +6,9 @@
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -31,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -43,22 +44,22 @@
              <a href="http://pylonsproject.org/">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
docs/tutorials/wiki/src/models/development.ini
@@ -1,24 +1,21 @@
[app:tutorial]
use = egg:tutorial
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
pyramid.include = pyramid_debugtoolbar
                  pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[pipeline:main]
pipeline =
    egg:WebError#evalerror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    tutorial
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[server:main]
use = egg:Paste#http
docs/tutorials/wiki/src/models/production.ini
@@ -1,11 +1,14 @@
[app:tutorial]
use = egg:tutorial
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
pyramid.include = pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[filter:weberror]
@@ -22,16 +25,10 @@
;smtp_use_tls =
;error_message =
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[pipeline:main]
pipeline =
    weberror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    tutorial
[server:main]
docs/tutorials/wiki/src/models/setup.py
@@ -9,8 +9,8 @@
requires = [
    'pyramid',
    'repoze.zodbconn',
    'repoze.tm2>=1.0b1', # default_commit_veto
    'repoze.retry',
    'pyramid_tm',
    'pyramid_debugtoolbar',
    'ZODB3',
    'WebError',
    ]
docs/tutorials/wiki/src/models/tutorial/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,7 +31,7 @@
body h3,
body h4,
body h5,
body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
@@ -6,8 +6,9 @@
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -31,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -43,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
docs/tutorials/wiki/src/tests/development.ini
@@ -1,24 +1,21 @@
[app:tutorial]
use = egg:tutorial
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
pyramid.include = pyramid_debugtoolbar
                  pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[pipeline:main]
pipeline =
    egg:WebError#evalerror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    tutorial
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[server:main]
use = egg:Paste#http
docs/tutorials/wiki/src/tests/production.ini
@@ -1,11 +1,14 @@
[app:tutorial]
use = egg:tutorial
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
pyramid.include = pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[filter:weberror]
@@ -22,16 +25,10 @@
;smtp_use_tls =
;error_message =
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[pipeline:main]
pipeline =
    weberror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    tutorial
[server:main]
docs/tutorials/wiki/src/tests/setup.py
@@ -9,8 +9,8 @@
requires = [
    'pyramid',
    'repoze.zodbconn',
    'repoze.tm2>=1.0b1',
    'repoze.retry',
    'pyramid_tm',
    'pyramid_debugtoolbar',
    'ZODB3',
    'WebError',
    'docutils',
docs/tutorials/wiki/src/tests/tutorial/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,7 +31,7 @@
body h3,
body h4,
body h5,
body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
@@ -6,8 +6,9 @@
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -31,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -43,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
docs/tutorials/wiki/src/views/development.ini
@@ -1,24 +1,21 @@
[app:tutorial]
use = egg:tutorial
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
pyramid.include = pyramid_debugtoolbar
                  pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[pipeline:main]
pipeline =
    egg:WebError#evalerror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    tutorial
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[server:main]
use = egg:Paste#http
docs/tutorials/wiki/src/views/production.ini
@@ -1,11 +1,14 @@
[app:tutorial]
use = egg:tutorial
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
pyramid.include = pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[filter:weberror]
@@ -22,16 +25,10 @@
;smtp_use_tls =
;error_message =
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[pipeline:main]
pipeline =
    weberror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    tutorial
[server:main]
docs/tutorials/wiki/src/views/setup.py
@@ -9,8 +9,8 @@
requires = [
    'pyramid',
    'repoze.zodbconn',
    'repoze.tm2>=1.0b1', # default_commit_veto
    'repoze.retry',
    'pyramid_tm',
    'pyramid_debugtoolbar',
    'ZODB3',
    'WebError',
    'docutils',
docs/tutorials/wiki/src/views/tutorial/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,7 +31,7 @@
body h3,
body h4,
body h5,
body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
@@ -6,8 +6,9 @@
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -31,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -43,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
docs/tutorials/wiki2/authorization.rst
@@ -54,7 +54,7 @@
``models.py`` file:
.. literalinclude:: src/authorization/tutorial/models.py
   :lines: 3-4,45-49
   :lines: 3-4,45-50
   :linenos:
   :language: python
@@ -228,7 +228,7 @@
.. code-block:: python
   :linenos:
   return dict(page = context,
   return dict(page = page,
               content = content,
               logged_in = logged_in,
               edit_url = edit_url)
docs/tutorials/wiki2/basiclayout.rst
@@ -57,7 +57,7 @@
``settings`` is passed to the Configurator as a keyword argument with the
dictionary values passed by PasteDeploy as the ``**settings`` argument.  This
will be a dictionary of settings parsed from the ``.ini`` file, which
contains deployment-related values such as ``reload_templates``,
contains deployment-related values such as ``pyramid.reload_templates``,
``db_string``, etc.
We now can call :meth:`pyramid.config.Configurator.add_static_view` with the
docs/tutorials/wiki2/src/authorization/development.ini
@@ -1,11 +1,11 @@
[app:tutorial]
use = egg:tutorial
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[pipeline:main]
docs/tutorials/wiki2/src/authorization/production.ini
@@ -1,11 +1,11 @@
[app:tutorial]
use = egg:tutorial
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[filter:weberror]
docs/tutorials/wiki2/src/authorization/tutorial/models.py
@@ -41,7 +41,7 @@
        transaction.commit()
    except IntegrityError:
        # already created
        pass
        transaction.abort()
class RootFactory(object):
    __acl__ = [ (Allow, Everyone, 'view'),
docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,7 +31,7 @@
body h3,
body h4,
body h5,
body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
@@ -6,8 +6,9 @@
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -31,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -43,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
docs/tutorials/wiki2/src/basiclayout/development.ini
@@ -1,11 +1,11 @@
[app:tutorial]
use = egg:tutorial
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[pipeline:main]
docs/tutorials/wiki2/src/basiclayout/production.ini
@@ -1,11 +1,11 @@
[app:tutorial]
use = egg:tutorial
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[filter:weberror]
docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
@@ -40,4 +40,4 @@
    try:
        populate()
    except IntegrityError:
        pass
        transaction.abort()
docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,7 +31,7 @@
body h3,
body h4,
body h5,
body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
@@ -6,8 +6,9 @@
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -31,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -43,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
docs/tutorials/wiki2/src/models/development.ini
@@ -1,11 +1,11 @@
[app:tutorial]
use = egg:tutorial
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[pipeline:main]
docs/tutorials/wiki2/src/models/production.ini
@@ -1,11 +1,11 @@
[app:tutorial]
use = egg:tutorial
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[filter:weberror]
docs/tutorials/wiki2/src/models/tutorial/models.py
@@ -39,4 +39,4 @@
        transaction.commit()
    except IntegrityError:
        # already created
        pass
        transaction.abort()
docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,7 +31,7 @@
body h3,
body h4,
body h5,
body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
@@ -6,8 +6,9 @@
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -31,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -43,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
docs/tutorials/wiki2/src/tests/development.ini
@@ -1,11 +1,11 @@
[app:tutorial]
use = egg:tutorial
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[pipeline:main]
docs/tutorials/wiki2/src/tests/production.ini
@@ -1,11 +1,11 @@
[app:tutorial]
use = egg:tutorial
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[filter:weberror]
docs/tutorials/wiki2/src/tests/tutorial/models.py
@@ -41,7 +41,7 @@
        transaction.commit()
    except IntegrityError:
        # already created
        pass
        transaction.abort()
class RootFactory(object):
    __acl__ = [ (Allow, Everyone, 'view'),
docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,7 +31,7 @@
body h3,
body h4,
body h5,
body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt
@@ -6,8 +6,9 @@
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -31,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -43,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
docs/tutorials/wiki2/src/views/development.ini
@@ -1,11 +1,11 @@
[app:tutorial]
use = egg:tutorial
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[pipeline:main]
docs/tutorials/wiki2/src/views/production.ini
@@ -1,11 +1,11 @@
[app:tutorial]
use = egg:tutorial
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[filter:weberror]
docs/tutorials/wiki2/src/views/tutorial/models.py
@@ -38,4 +38,4 @@
        transaction.commit()
    except IntegrityError:
        # already created
        pass
        transaction.abort()
docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,7 +31,7 @@
body h3,
body h4,
body h5,
body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
@@ -6,8 +6,9 @@
  <meta name="keywords" content="python web application" />
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('tutorial:static/favicon.ico')}" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="${request.static_url('tutorial:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('tutorial:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -31,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -43,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
docs/whatsnew-1.0.rst
@@ -62,8 +62,8 @@
However, you won't have to do much to use your existing BFG applications on
Pyramid. There's automation which will change most of your import statements
and ZCML declarations. See
http://docs.pylonshq.com/pyramid/dev/tutorials/bfg/index.html for upgrade
instructions.
http://docs.pylonsproject.org/projects/pyramid/current/tutorials/bfg/index.html
for upgrade instructions.
Pylons 1 users will need to do more work to use Pyramid, as Pyramid shares no
"DNA" with Pylons.  It is hoped that over time documentation and upgrade code
docs/whatsnew-1.1.rst
@@ -29,6 +29,12 @@
- Default HTTP exception view.
- ``http_cache`` view configuration parameter causes Pyramid to set HTTP
  caching headers.
- Features that make it easier to write scripts that work in a :app:`Pyramid`
  environment.
``request.response``
~~~~~~~~~~~~~~~~~~~~
@@ -91,64 +97,109 @@
  exception to that of Pyramid 1.0 (the exception will propagate to
  middleware and to the WSGI server).
``http_cache``
~~~~~~~~~~~~~~
A new value ``http_cache`` can be used as a :term:`view configuration`
parameter.
When you supply an ``http_cache`` value to a view configuration, the
``Expires`` and ``Cache-Control`` headers of a response generated by the
associated view callable are modified.  The value for ``http_cache`` may be
one of the following:
- A nonzero integer.  If it's a nonzero integer, it's treated as a number
  of seconds.  This number of seconds will be used to compute the
  ``Expires`` header and the ``Cache-Control: max-age`` parameter of
  responses to requests which call this view.  For example:
  ``http_cache=3600`` instructs the requesting browser to 'cache this
  response for an hour, please'.
- A ``datetime.timedelta`` instance.  If it's a ``datetime.timedelta``
  instance, it will be converted into a number of seconds, and that number
  of seconds will be used to compute the ``Expires`` header and the
  ``Cache-Control: max-age`` parameter of responses to requests which call
  this view.  For example: ``http_cache=datetime.timedelta(days=1)``
  instructs the requesting browser to 'cache this response for a day,
  please'.
- Zero (``0``).  If the value is zero, the ``Cache-Control`` and
  ``Expires`` headers present in all responses from this view will be
  composed such that client browser cache (and any intermediate caches) are
  instructed to never cache the response.
- A two-tuple.  If it's a two tuple (e.g. ``http_cache=(1,
  {'public':True})``), the first value in the tuple may be a nonzero
  integer or a ``datetime.timedelta`` instance; in either case this value
  will be used as the number of seconds to cache the response.  The second
  value in the tuple must be a dictionary.  The values present in the
  dictionary will be used as input to the ``Cache-Control`` response
  header.  For example: ``http_cache=(3600, {'public':True})`` means 'cache
  for an hour, and add ``public`` to the Cache-Control header of the
  response'.  All keys and values supported by the
  ``webob.cachecontrol.CacheControl`` interface may be added to the
  dictionary.  Supplying ``{'public':True}`` is equivalent to calling
  ``response.cache_control.public = True``.
Providing a non-tuple value as ``http_cache`` is equivalent to calling
``response.cache_expires(value)`` within your view's body.
Providing a two-tuple value as ``http_cache`` is equivalent to calling
``response.cache_expires(value[0], **value[1])`` within your view's body.
If you wish to avoid influencing, the ``Expires`` header, and instead wish
to only influence ``Cache-Control`` headers, pass a tuple as ``http_cache``
with the first element of ``None``, e.g.: ``(None, {'public':True})``.
The environment setting ``PYRAMID_PREVENT_HTTP_CACHE`` and configuration
file value ``prevent_http_cache`` are synonymous and allow you to prevent
HTTP cache headers from being set by Pyramid's ``http_cache`` machinery
globally in a process.  see :ref:`influencing_http_caching` and
:ref:`preventing_http_caching`.
Easier Scripting Writing
~~~~~~~~~~~~~~~~~~~~~~~~
A new API function :func:`pyramid.paster.bootstrap` has been added to make
writing scripts that need to work under Pyramid environment easier, e.g.:
.. code-block:: python
    from pyramid.paster import bootstrap
    info = bootstrap('/path/to/my/development.ini')
    request = info['request']
    print request.route_url('myroute')
See :ref:`writing_a_script` for more details.
Minor Feature Additions
-----------------------
- New request attribute: ``json``. If the request's ``content_type`` is
  ``application/json``, this attribute will contain the JSON-decoded
  variant of the request body.  If the request's ``content_type`` is not
  ``application/json``, this attribute will be ``None``.
- It is now possible to invoke ``paster pshell`` even if the paste ini file
  section name pointed to in its argument is not actually a Pyramid WSGI
  application.  The shell will work in a degraded mode, and will warn the
  user.  See "The Interactive Shell" in the "Creating a Pyramid Project"
  narrative documentation section.
- A new value ``http_cache`` can be used as a :term:`view configuration`
  parameter.
- The ``paster pshell``, ``paster pviews``, and ``paster proutes`` commands
  each now under the hood uses :func:`pyramid.paster.bootstrap`, which makes
  it possible to supply an ``.ini`` file without naming the "right" section
  in the file that points at the actual Pyramid application.  Instead, you
  can generally just run ``paster {pshell|proutes|pviews} development.ini``
  and it will do mostly the right thing.
  When you supply an ``http_cache`` value to a view configuration, the
  ``Expires`` and ``Cache-Control`` headers of a response generated by the
  associated view callable are modified.  The value for ``http_cache`` may be
  one of the following:
- It is now possible to add a ``[pshell]`` section to your application's .ini
  configuration file, which influences the global names available to a pshell
  session.  See :ref:`extending_pshell`.
  - A nonzero integer.  If it's a nonzero integer, it's treated as a number
    of seconds.  This number of seconds will be used to compute the
    ``Expires`` header and the ``Cache-Control: max-age`` parameter of
    responses to requests which call this view.  For example:
    ``http_cache=3600`` instructs the requesting browser to 'cache this
    response for an hour, please'.
- The :meth:`pyramid.config.Configurator.scan` method has grown a ``**kw``
  argument.  ``kw`` argument represents a set of keyword arguments to pass to
  the Venusian ``Scanner`` object created by Pyramid.  (See the
  :term:`Venusian` documentation for more information about ``Scanner``).
  - A ``datetime.timedelta`` instance.  If it's a ``datetime.timedelta``
    instance, it will be converted into a number of seconds, and that number
    of seconds will be used to compute the ``Expires`` header and the
    ``Cache-Control: max-age`` parameter of responses to requests which call
    this view.  For example: ``http_cache=datetime.timedelta(days=1)``
    instructs the requesting browser to 'cache this response for a day,
    please'.
  - Zero (``0``).  If the value is zero, the ``Cache-Control`` and
    ``Expires`` headers present in all responses from this view will be
    composed such that client browser cache (and any intermediate caches) are
    instructed to never cache the response.
  - A two-tuple.  If it's a two tuple (e.g. ``http_cache=(1,
    {'public':True})``), the first value in the tuple may be a nonzero
    integer or a ``datetime.timedelta`` instance; in either case this value
    will be used as the number of seconds to cache the response.  The second
    value in the tuple must be a dictionary.  The values present in the
    dictionary will be used as input to the ``Cache-Control`` response
    header.  For example: ``http_cache=(3600, {'public':True})`` means 'cache
    for an hour, and add ``public`` to the Cache-Control header of the
    response'.  All keys and values supported by the
    ``webob.cachecontrol.CacheControl`` interface may be added to the
    dictionary.  Supplying ``{'public':True}`` is equivalent to calling
    ``response.cache_control.public = True``.
  Providing a non-tuple value as ``http_cache`` is equivalent to calling
  ``response.cache_expires(value)`` within your view's body.
  Providing a two-tuple value as ``http_cache`` is equivalent to calling
  ``response.cache_expires(value[0], **value[1])`` within your view's body.
  If you wish to avoid influencing, the ``Expires`` header, and instead wish
  to only influence ``Cache-Control`` headers, pass a tuple as ``http_cache``
  with the first element of ``None``, e.g.: ``(None, {'public':True})``.
- New request property: ``json_body``. This property will return the
  JSON-decoded variant of the request body.  If the request body is not
  well-formed JSON, this property will raise an exception.
- A `JSONP <http://en.wikipedia.org/wiki/JSONP>`_ renderer.  See
  :ref:`jsonp_renderer` for more details.
@@ -228,6 +279,54 @@
  preprocessor to be specified as a Python callable or Python dotted name.
  See https://github.com/Pylons/pyramid/pull/183 for rationale.
- New API class: :class:`pyramid.static.static_view`.  This supersedes the
  (now deprecated) :class:`pyramid.view.static` class.
  :class:`pyramid.static.static_view`, by default, serves up documents as the
  result of the request's ``path_info``, attribute rather than it's
  ``subpath`` attribute (the inverse was true of
  :class:`pyramid.view.static`, and still is).
  :class:`pyramid.static.static_view` exposes a ``use_subpath`` flag for use
  when you want the static view to behave like the older deprecated version.
- A new api function :func:`pyramid.scripting.prepare` has been added.  It is
  a lower-level analogue of :func:`pyramid.paster.boostrap` that accepts a
  request and a registry instead of a config file argument, and is used for
  the same purpose:
  .. code-block:: python
      from pyramid.scripting import prepare
      info = prepare(registry=myregistry)
      request = info['request']
      print request.route_url('myroute')
- A new API function :func:`pyramid.scripting.make_request` has been added.
  The resulting request will have a ``registry`` attribute.  It is meant to
  be used in conjunction with :func:`pyramid.scripting.prepare` and/or
  :func:`pyramid.paster.bootstrap` (both of which accept a request as an
  argument):
  .. code-block:: python
      from pyramid.scripting import make_request
      request = make_request('/')
- New API attribute :attr:`pyramid.config.global_registries` is an iterable
  object that contains references to every Pyramid registry loaded into the
  current process via :meth:`pyramid.config.Configurator.make_app`.  It also
  has a ``last`` attribute containing the last registry loaded.  This is used
  by the scripting machinery, and is available for introspection.
- Added the :attr:`pyramid.renderers.null_renderer` object as an API.  The
  null renderer is an object that can be used in advanced integration cases
  as input to the view configuration ``renderer=`` argument.  When the null
  renderer is used as a view renderer argument, Pyramid avoids converting the
  view callable result into a Response object.  This is useful if you want to
  reuse the view configuration and lookup machinery outside the context of
  its use by the Pyramid router.  (This feature was added for consumption by
  the ``pyramid_rpc`` package, which uses view configuration and lookup
  outside the context of a router in exactly this way.)
Backwards Incompatibilities
---------------------------
@@ -290,6 +389,26 @@
Deprecations and Behavior Differences
-------------------------------------
.. note:: Under Python 2.7+, it's necessary to pass the Python interpreter
   the correct warning flags to see deprecation warnings emitted by Pyramid
   when porting your application from an older version of Pyramid.  Use the
   ``PYTHONWARNINGS`` environment variable with the value ``all`` in the
   shell you use to invoke ``paster serve`` to see these warnings, e.g. on
   UNIX, ``PYTHONWARNINGS=all bin/paster serve development.ini``.  Python 2.5
   and 2.6 show deprecation warnings by default, so this is unecessary there.
   All deprecation warnings are emitted to the console.
- The :class:`pyramid.view.static` class has been deprecated in favor of the
  newer :class:`pyramid.static.static_view` class.  A deprecation warning is
  raised when it is used.  You should replace it with a reference to
  :class:`pyramid.static.static_view` with the ``use_subpath=True`` argument.
- The ``paster pshell``, ``paster proutes``, and ``paster pviews`` commands
  now take a single argument in the form ``/path/to/config.ini#sectionname``
  rather than the previous 2-argument spelling ``/path/to/config.ini
  sectionname``.  ``#sectionname`` may be omitted, in which case ``#main`` is
  assumed.
- The default Mako renderer is now configured to escape all HTML in
  expression tags. This is intended to help prevent XSS attacks caused by
@@ -360,7 +479,7 @@
  these methods will be removed entirely.
- A custom request factory is now required to return a request object that
  has a ``response`` attribute (or "reified"/lazy property) if they the
  has a ``response`` attribute (or "reified"/lazy property) if the
  request is meant to be used in a view that uses a renderer.  This
  ``response`` attribute should be an instance of the class
  :class:`pyramid.response.Response`.
@@ -423,8 +542,8 @@
- Deprecated the
  :meth:`pyramid.config.Configurator.set_renderer_globals_factory` method and
  the ``renderer_globals`` Configurator constructor parameter.  Users should
  use convert code using this feature to use a BeforeRender event als
  :ref:`beforerender_event`.
  convert code using this feature to use a BeforeRender event. See the section
  :ref:`beforerender_event` in the Hooks chapter.
- In Pyramid 1.0, the :class:`pyramid.events.subscriber` directive behaved
  contrary to the documentation when passed more than one interface object to
@@ -449,6 +568,31 @@
     def expects_object_event(object, event):
         print object, event
- In 1.0, if a :class:`pyramid.events.BeforeRender` event subscriber added a
  value via the ``__setitem__`` or ``update`` methods of the event object
  with a key that already existed in the renderer globals dictionary, a
  ``KeyError`` was raised.  With the deprecation of the
  "add_renderer_globals" feature of the configurator, there was no way to
  override an existing value in the renderer globals dictionary that already
  existed.  Now, the event object will overwrite an older value that is
  already in the globals dictionary when its ``__setitem__`` or ``update`` is
  called (as well as the new ``setdefault`` method), just like a plain old
  dictionary.  As a result, for maximum interoperability with other
  third-party subscribers, if you write an event subscriber meant to be used
  as a BeforeRender subscriber, your subscriber code will now need to (using
  ``.get`` or ``__contains__`` of the event object) ensure no value already
  exists in the renderer globals dictionary before setting an overriding
  value.
- The :meth:`pyramid.config.Configurator.add_route` method allowed two routes
  with the same route to be added without an intermediate call to
  :meth:`pyramid.config.Configurator.commit`.  If you now receive a
  ``ConfigurationError`` at startup time that appears to be ``add_route``
  related, you'll need to either a) ensure that all of your route names are
  unique or b) call ``config.commit()`` before adding a second route with the
  name of a previously added name or c) use a Configurator that works in
  ``autocommit`` mode.
Dependency Changes
------------------
@@ -461,6 +605,9 @@
Documentation Enhancements
--------------------------
- Added a section entitled :ref:`writing_a_script` to the "Command-Line
  Pyramid" chapter.
- The :ref:`bfg_wiki_tutorial` was updated slightly.
- The :ref:`bfg_sql_wiki_tutorial` was updated slightly.
pyramid/compat.py
New file
@@ -0,0 +1,8 @@
try:
    import json
except ImportError: # pragma: no cover
    try:
        import simplejson as json
    except NotImplementedError:
        from django.utils import simplejson as json # GAE
pyramid/compat/__init__.py
File was deleted
pyramid/config.py
@@ -1,10 +1,12 @@
import inspect
import logging
import os
import re
import sys
import types
import traceback
import warnings
from hashlib import md5
import venusian
@@ -36,6 +38,7 @@
from pyramid.interfaces import IRendererGlobalsFactory
from pyramid.interfaces import IRequest
from pyramid.interfaces import IRequestFactory
from pyramid.interfaces import ITweens
from pyramid.interfaces import IResponse
from pyramid.interfaces import IRootFactory
from pyramid.interfaces import IRouteRequest
@@ -52,9 +55,6 @@
from pyramid import renderers
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.compat import all
from pyramid.compat import md5
from pyramid.compat import any
from pyramid.events import ApplicationCreated
from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import PredicateMismatch
@@ -62,7 +62,6 @@
from pyramid.httpexceptions import HTTPForbidden
from pyramid.httpexceptions import HTTPNotFound
from pyramid.i18n import get_localizer
from pyramid.log import make_stream_logger
from pyramid.mako_templating import renderer_factory as mako_renderer_factory
from pyramid.path import caller_package
from pyramid.path import package_path
@@ -72,6 +71,7 @@
from pyramid.request import route_request_iface
from pyramid.asset import PackageOverrides
from pyramid.asset import resolve_asset_spec
from pyramid.security import NO_PERMISSION_REQUIRED
from pyramid.settings import Settings
from pyramid.static import StaticURLInfo
from pyramid.threadlocal import get_current_registry
@@ -80,8 +80,13 @@
from pyramid.traversal import DefaultRootFactory
from pyramid.traversal import find_interface
from pyramid.traversal import traversal_path
from pyramid.tweens import excview_tween_factory
from pyramid.tweens import Tweens
from pyramid.tweens import tween_factory_name
from pyramid.tweens import MAIN, INGRESS, EXCVIEW
from pyramid.urldispatch import RoutesMapper
from pyramid.util import DottedNameResolver
from pyramid.util import WeakOrderedSet
from pyramid.view import render_view_to_response
DEFAULT_RENDERERS = (
@@ -137,7 +142,7 @@
    The Configurator accepts a number of arguments: ``registry``,
    ``package``, ``settings``, ``root_factory``, ``authentication_policy``,
    ``authorization_policy``, ``renderers`` ``debug_logger``,
    ``authorization_policy``, ``renderers``, ``debug_logger``,
    ``locale_negotiator``, ``request_factory``, ``renderer_globals_factory``,
    ``default_permission``, ``session_factory``, ``default_view_mapper``,
    ``autocommit``, and ``exceptionresponse_view``.
@@ -194,12 +199,12 @@
    :meth:`pyramid.config.Configurator.add_renderer`).  If
    it is not passed, a default set of renderer factories is used.
    If ``debug_logger`` is not passed, a default debug logger that
    logs to stderr will be used.  If it is passed, it should be an
    instance of the :class:`logging.Logger` (PEP 282) standard library
    class or a :term:`dotted Python name` to same.  The debug logger
    is used by :app:`Pyramid` itself to log warnings and
    authorization debugging information.
    If ``debug_logger`` is not passed, a default debug logger that logs to a
    logger will be used (the logger name will be the package name of the
    *caller* of this configurator).  If it is passed, it should be an
    instance of the :class:`logging.Logger` (PEP 282) standard library class
    or a Python logger name.  The debug logger is used by :app:`Pyramid`
    itself to log warnings and authorization debugging information.
    If ``locale_negotiator`` is passed, it should be a :term:`locale
    negotiator` implementation or a :term:`dotted Python name` to
@@ -469,7 +474,7 @@
        :meth:`pyramid.config.Configuration.commit` is called (or executed
        immediately if ``autocommit`` is ``True``).
        .. note:: This method is typically only used by :app:`Pyramid`
        .. warning:: This method is typically only used by :app:`Pyramid`
           framework extension authors, not by :app:`Pyramid` application
           developers.
@@ -632,6 +637,10 @@
        """
        Add a directive method to the configurator.
        .. warning:: This method is typically only used by :app:`Pyramid`
           framework extension authors, not by :app:`Pyramid` application
           developers.
        Framework extenders can add directive methods to a configurator by
        instructing their users to call ``config.add_directive('somename',
        'some.callable')``.  This will make ``some.callable`` accessible as
@@ -683,7 +692,7 @@
        """ Return a new Configurator instance with the same registry
        as this configurator using the package supplied as the
        ``package`` argument to the new configurator.  ``package`` may
        be an actual Python package object or a Python dotted name
        be an actual Python package object or a :term:`dotted Python name`
        representing a package."""
        context = self._ctx
        if context is None:
@@ -739,6 +748,13 @@
        policies, renderers, a debug logger, a locale negotiator, and various
        other settings using the configurator's current registry, as per the
        descriptions in the Configurator constructor."""
        tweens = []
        includes = []
        if settings:
            includes = [x.strip() for x in
                        settings.get('pyramid.include', '').splitlines()]
            tweens =   [x.strip() for x in
                        settings.get('pyramid.tweens','').splitlines()]
        registry = self.registry
        self._fix_registry()
        self._set_settings(settings)
@@ -748,9 +764,12 @@
        # cope with WebOb exc objects not decoratored with IExceptionResponse
        from webob.exc import WSGIHTTPException as WebobWSGIHTTPException
        registry.registerSelfAdapter((WebobResponse,), IResponse)
        debug_logger = self.maybe_dotted(debug_logger)
        if debug_logger is None:
            debug_logger = make_stream_logger('pyramid.debug', sys.stderr)
            debug_logger = logging.getLogger(self.package_name)
        elif isinstance(debug_logger, basestring):
            debug_logger = logging.getLogger(debug_logger)
        registry.registerUtility(debug_logger, IDebugLogger)
        if authentication_policy or authorization_policy:
            self._set_security_policies(authentication_policy,
@@ -791,6 +810,10 @@
        if default_view_mapper is not None:
            self.set_view_mapper(default_view_mapper)
            self.commit()
        for inc in includes:
            self.include(inc)
        for factory in tweens:
            self._add_tween(factory, explicit=True)
        
    def hook_zca(self):
        """ Call :func:`zope.component.getSiteManager.sethook` with
@@ -834,10 +857,13 @@
    def derive_view(self, view, attr=None, renderer=None):
        """
        Create a :term:`view callable` using the function, instance,
        or class (or :term:`dotted Python name` referring to the same)
        provided as ``view`` object.
        .. warning:: This method is typically only used by :app:`Pyramid`
           framework extension authors, not by :app:`Pyramid` application
           developers.
        This is API is useful to framework extenders who create
        pluggable systems which need to register 'proxy' view
@@ -906,6 +932,123 @@
        :term:`response` object.  """
        return self._derive_view(view, attr=attr, renderer=renderer)
    @action_method
    def add_tween(self, tween_factory, alias=None, under=None, over=None):
        """
        Add a 'tween factory'.  A :term:`tween` (a contraction of 'between')
        is a bit of code that sits between the Pyramid router's main request
        handling function and the upstream WSGI component that uses
        :app:`Pyramid` as its 'app'.  This is a feature that may be used by
        Pyramid framework extensions, to provide, for example,
        Pyramid-specific view timing support, bookkeeping code that examines
        exceptions before they are returned to the upstream WSGI application,
        or a variety of other features.  Tweens behave a bit like
        :term:`WSGI` 'middleware' but they have the benefit of running in a
        context in which they have access to the Pyramid :term:`application
        registry` as well as the Pyramid rendering machinery.
        .. note:: You can view the tween ordering configured into a given
                  Pyramid application by using the ``paster ptweens``
                  command.  See :ref:`displaying_tweens`.
        The ``alias`` argument, if it is not ``None``, should be a string.
        The string will represent a value that other callers of ``add_tween``
        may pass as an ``under`` and ``over`` argument instead of a dotted
        name to a tween factory.
        The ``under`` and ``over`` arguments allow the caller of
        ``add_tween`` to provide a hint about where in the tween chain this
        tween factory should be placed when an implicit tween chain is used.
        These hints are only used used when an explicit tween chain is not
        used (when the ``pyramid.tweens`` configuration value is not set).
        Allowable values for ``under`` or ``over`` (or both) are:
        - ``None`` (the default).
        - A :term:`dotted Python name` to a tween factory: a string
          representing the predicted dotted name of a tween factory added in
          a call to ``add_tween`` in the same configuration session.
        - A tween alias: a string representing the predicted value of
          ``alias`` in a separate call to ``add_tween`` in the same
          configuration session
        - One of the constants :attr:`pyramid.tweens.MAIN`,
          :attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`.
        ``under`` means 'closer to the main Pyramid application than',
        ``over`` means 'closer to the request ingress than'.
        For example, calling ``add_tween(factory, over=pyramid.tweens.MAIN)``
        will attempt to place the tween factory represented by ``factory``
        directly 'above' (in ``paster ptweens`` order) the main Pyramid
        request handler.  Likewise, calling ``add_tween(factory,
        over=pyramid.tweens.MAIN, under='someothertween')`` will attempt to
        place this tween factory 'above' the main handler but 'below' (a
        fictional) 'someothertween' tween factory (which was presumably added
        via ``add_tween(factory, alias='someothertween')``).
        If an ``under`` or ``over`` value is provided that does not match a
        tween factory dotted name or alias in the current configuration, that
        value will be ignored.  It is not an error to provide an ``under`` or
        ``over`` value that matches an unused tween factory.
        Specifying neither ``over`` nor ``under`` is equivalent to specifying
        ``under=INGRESS``.
        Implicit tween ordering is obviously only best-effort.  Pyramid will
        attempt to present an implicit order of tweens as best it can, but
        the only surefire way to get any particular ordering is to use an
        explicit tween order.  A user may always override the implicit tween
        ordering by using an explicit ``pyramid.tweens`` configuration value
        setting.
        ``alias``, ``under``, and ``over`` arguments are ignored when an
        explicit tween chain is specified using the ``pyramid.tweens``
        configuration value.
        For more information, see :ref:`registering_tweens`.
        .. note:: This feature is new as of Pyramid 1.1.1.
        """
        return self._add_tween(tween_factory, alias=alias, under=under,
                               over=over, explicit=False)
    def _add_tween(self, tween_factory, alias=None, under=None, over=None,
                   explicit=False):
        tween_factory = self.maybe_dotted(tween_factory)
        name = tween_factory_name(tween_factory)
        if alias in (MAIN, INGRESS):
            raise ConfigurationError('%s is a reserved tween name' % alias)
        if over is INGRESS:
            raise ConfigurationError('%s cannot be over INGRESS' % name)
        if under is MAIN:
            raise ConfigurationError('%s cannot be under MAIN' % name)
        registry = self.registry
        tweens = registry.queryUtility(ITweens)
        if tweens is None:
            tweens = Tweens()
            registry.registerUtility(tweens, ITweens)
            tweens.add_implicit(tween_factory_name(excview_tween_factory),
                                excview_tween_factory, alias=EXCVIEW,
                                over=MAIN)
        if explicit:
            tweens.add_explicit(name, tween_factory)
        else:
            tweens.add_implicit(name, tween_factory, alias=alias, under=under,
                                over=over)
        self.action(('tween', name, explicit))
        if not explicit and alias is not None:
            self.action(('tween', alias, explicit))
    @action_method
    def add_request_handler(self, factory, name): # pragma: no cover
        # XXX bw compat for debugtoolbar
        return self._add_tween(factory, explicit=False)
    @action_method
    def add_subscriber(self, subscriber, iface=None):
        """Add an event :term:`subscriber` for the event stream
@@ -1003,11 +1146,14 @@
    def make_wsgi_app(self):
        """ Commits any pending configuration statements, sends a
        :class:`pyramid.events.ApplicationCreated` event to all listeners,
        and returns a :app:`Pyramid` WSGI application representing the
        committed configuration state."""
        adds this configuration's registry to
        :attr:`pyramid.config.global_registries`, and returns a
        :app:`Pyramid` WSGI application representing the committed
        configuration state."""
        self.commit()
        from pyramid.router import Router # avoid circdep
        app = Router(self.registry)
        global_registries.add(self.registry)
        # We push the registry on to the stack here in case any code
        # that depends on the registry threadlocal APIs used in
        # listeners subscribed to the IApplicationCreated event.
@@ -1016,6 +1162,7 @@
            self.registry.notify(ApplicationCreated(app))
        finally:
            self.manager.pop()
        return app
    @action_method
@@ -1057,10 +1204,10 @@
          ``default_permission`` argument, or if
          :meth:`pyramid.config.Configurator.set_default_permission`
          was used prior to this view registration.  Pass the string
          ``__no_permission_required__`` as the permission argument to
          explicitly indicate that the view should always be
          executable by entirely anonymous users, regardless of the
          default permission, bypassing any :term:`authorization
          :data:`pyramid.security.NO_PERMISSION_REQUIRED` as the
          permission argument to explicitly indicate that the view should
          always be executable by entirely anonymous users, regardless of
          the default permission, bypassing any :term:`authorization
          policy` that may be in effect.
        attr
@@ -1116,6 +1263,8 @@
        http_cache
          .. note:: This feature is new as of Pyramid 1.1.
          When you supply an ``http_cache`` value to a view configuration,
          the ``Expires`` and ``Cache-Control`` headers of a response
          generated by the associated view callable are modified.  The value
@@ -1166,6 +1315,12 @@
          instead wish to only influence ``Cache-Control`` headers, pass a
          tuple as ``http_cache`` with the first element of ``None``, e.g.:
          ``(None, {'public':True})``.
          If you wish to prevent a view that uses ``http_cache`` in its
          configuration from having its caching response headers changed by
          this machinery, set ``response.cache_control.prevent_auto = True``
          before returning the response from the view.  This effectively
          disables any HTTP caching done by ``http_cache`` for that response.
        wrapper
@@ -1229,18 +1384,7 @@
          This value must match the ``name`` of a :term:`route
          configuration` declaration (see :ref:`urldispatch_chapter`)
          that must match before this view will be called.  Note that
          the ``route`` configuration referred to by ``route_name``
          usually has a ``*traverse`` token in the value of its
          ``path``, representing a part of the path that will be used
          by :term:`traversal` against the result of the route's
          :term:`root factory`.
          .. warning:: Using this argument services an advanced
             feature that isn't often used unless you want to perform
             traversal *after* a route has matched. See
             :ref:`hybrid_chapter` for more information on using this
             advanced feature.
          that must match before this view will be called.
        request_type
@@ -1823,12 +1967,11 @@
           should only be used to support older code bases which depend upon
           them.* Use a separate call to
           :meth:`pyramid.config.Configurator.add_view` to associate a view
           with a route.  See :ref:`add_route_view_config` for more info.
           with a route using the ``route_name`` argument.
        view
          .. warning:: Deprecated as of :app:`Pyramid` 1.1; see
             :ref:`add_route_view_config`.
          .. warning:: Deprecated as of :app:`Pyramid` 1.1.
          A Python object or :term:`dotted Python name` to the same
          object that will be used as a view callable when this route
@@ -1836,9 +1979,8 @@
        view_context
          .. warning:: Deprecated as of :app:`Pyramid` 1.1; see
             :ref:`add_route_view_config`.
          .. warning:: Deprecated as of :app:`Pyramid` 1.1.
          A class or an :term:`interface` or :term:`dotted Python
          name` to the same object which the :term:`context` of the
          view should match for the view named by the route to be
@@ -1853,8 +1995,7 @@
        view_permission
          .. warning:: Deprecated as of :app:`Pyramid` 1.1; see
             :ref:`add_route_view_config`.
          .. warning:: Deprecated as of :app:`Pyramid` 1.1.
          The permission name required to invoke the view associated
          with this route.  e.g. ``edit``. (see
@@ -1868,8 +2009,7 @@
        view_renderer
          .. warning:: Deprecated as of :app:`Pyramid` 1.1; see
             :ref:`add_route_view_config`.
          .. warning:: Deprecated as of :app:`Pyramid` 1.1.
          This is either a single string term (e.g. ``json``) or a
          string implying a path or :term:`asset specification`
@@ -1893,8 +2033,7 @@
        view_attr
          .. warning:: Deprecated as of :app:`Pyramid` 1.1; see
             :ref:`add_route_view_config`.
          .. warning:: Deprecated as of :app:`Pyramid` 1.1.
          The view machinery defaults to using the ``__call__`` method
          of the view callable (or the function itself, if the view
@@ -1961,11 +2100,7 @@
        if self.route_prefix:
            pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/')
        discriminator = ['route', name, xhr, request_method, path_info,
                         request_param, header, accept]
        discriminator.extend(sorted(custom_predicates))
        discriminator = tuple(discriminator)
        discriminator = ('route', name)
        self.action(discriminator, None)
        return mapper.connect(name, pattern, factory, predicates=predicates,
@@ -1981,7 +2116,7 @@
        return mapper
    # this is *not* an action method (uses caller_package)
    def scan(self, package=None, categories=None):
    def scan(self, package=None, categories=None, **kw):
        """Scan a Python package and any of its subpackages for objects
        marked with :term:`configuration decoration` such as
        :class:`pyramid.view.view_config`.  Any decorated object found will
@@ -2001,12 +2136,28 @@
        :class:`pyramid.view.view_config`.  See the :term:`Venusian`
        documentation for more information about limiting a scan by using an
        explicit set of categories.
        To perform a ``scan``, Pyramid creates a Venusian ``Scanner`` object.
        The ``kw`` argument represents a set of keyword arguments to pass to
        the Venusian ``Scanner`` object's constructor.  See the
        :term:`venusian` documentation (its ``Scanner`` class) for more
        information about the constructor.  By default, the only keyword
        arguments passed to the Scanner constructor are ``{'config':self}``
        where ``self`` is this configurator object.  This services the
        requirement of all built-in Pyramid decorators, but extension systems
        may require additional arguments.  Providing this argument is not
        often necessary; it's an advanced usage.
        .. note:: the ``**kw`` argument is new in Pyramid 1.1
        """
        package = self.maybe_dotted(package)
        if package is None: # pragma: no cover
            package = caller_package()
        scanner = self.venusian.Scanner(config=self)
        scankw = {'config':self}
        scankw.update(kw)
        scanner = self.venusian.Scanner(**scankw)
        scanner.scan(package, categories=categories)
    @action_method
@@ -2141,7 +2292,7 @@
            context = getattr(request, 'context', None)
            return view(context, request)
        return self.add_view(bwcompat_view, context=HTTPForbidden,
                             wrapper=wrapper)
                             wrapper=wrapper, renderer=renderer)
    @action_method
    def set_notfound_view(self, view=None, attr=None, renderer=None,
@@ -2182,7 +2333,7 @@
            context = getattr(request, 'context', None)
            return view(context, request)
        return self.add_view(bwcompat_view, context=HTTPNotFound,
                             wrapper=wrapper)
                             wrapper=wrapper, renderer=renderer)
    @action_method
    def set_request_factory(self, factory):
@@ -2287,10 +2438,11 @@
          If a default permission is in effect, view configurations meant to
          create a truly anonymously accessible view (even :term:`exception
          view` views) *must* use the explicit permission string
          ``__no_permission_required__`` as the permission.  When this string
          is used as the ``permission`` for a view configuration, the default
          permission is ignored, and the view is registered, making it
          available to all callers regardless of their credentials.
          :data:`pyramid.security.NO_PERMISSION_REQUIRED` as the permission.
          When this string is used as the ``permission`` for a view
          configuration, the default permission is ignored, and the view is
          registered, making it available to all callers regardless of their
          credentials.
        See also :ref:`setting_a_default_permission`.
@@ -2417,10 +2569,10 @@
        The ``permission`` keyword argument is used to specify the
        :term:`permission` required by a user to execute the static view.  By
        default, it is the string ``__no_permission_required__``.  The
        ``__no_permission_required__`` string is a special sentinel which
        indicates that, even if a :term:`default permission` exists for the
        current application, the static view should be renderered to
        default, it is the string
        :data:`pyramid.security.NO_PERMISSION_REQUIRED`, a special sentinel
        which indicates that, even if a :term:`default permission` exists for
        the current application, the static view should be renderered to
        completely anonymous users.  This default value is permissive
        because, in most web apps, static assets seldom need protection from
        viewing.  If ``permission`` is specified, the security checking will
@@ -2897,54 +3049,42 @@
                continue
        raise PredicateMismatch(self.name)
def wraps_view(wrapped):
def wraps_view(wrapper):
    def inner(self, view):
        wrapped_view = wrapped(self, view)
        return preserve_view_attrs(view, wrapped_view)
        wrapper_view = wrapper(self, view)
        return preserve_view_attrs(view, wrapper_view)
    return inner
def preserve_view_attrs(view, wrapped_view):
    if wrapped_view is view:
def preserve_view_attrs(view, wrapper):
    if wrapper is view:
        return view
    original_view = getattr(view, '__original_view__', None)
    if original_view is None:
        original_view = view
    wrapped_view.__original_view__ = original_view
    wrapped_view.__module__ = view.__module__
    wrapped_view.__doc__ = view.__doc__
    wrapper.__wraps__ = view
    wrapper.__original_view__ = original_view
    wrapper.__module__ = view.__module__
    wrapper.__doc__ = view.__doc__
    try:
        wrapped_view.__name__ = view.__name__
        wrapper.__name__ = view.__name__
    except AttributeError:
        wrapped_view.__name__ = repr(view)
    try:
        wrapped_view.__permitted__ = view.__permitted__
    except AttributeError:
        pass
    try:
        wrapped_view.__call_permissive__ = view.__call_permissive__
    except AttributeError:
        pass
    try:
        wrapped_view.__permission__ = view.__permission__
    except AttributeError:
        pass
    try:
        wrapped_view.__predicated__ = view.__predicated__
    except AttributeError:
        pass
    try:
        wrapped_view.__predicates__ = view.__predicates__
    except AttributeError:
        pass
    try:
        wrapped_view.__accept__ = view.__accept__
    except AttributeError:
        pass
    try:
        wrapped_view.__order__ = view.__order__
    except AttributeError:
        pass
    return wrapped_view
        wrapper.__name__ = repr(view)
    # attrs that may not exist on "view", but, if so, must be attached to
    # "wrapped view"
    for attr in ('__permitted__', '__call_permissive__', '__permission__',
                 '__predicated__', '__predicates__', '__accept__',
                 '__order__'):
        try:
            setattr(wrapper, attr, getattr(view, attr))
        except AttributeError:
            pass
    return wrapper
class ViewDeriver(object):
    def __init__(self, **kw):
@@ -2960,8 +3100,8 @@
                self.authdebug_view(
                    self.secured_view(
                        self.owrapped_view(
                            self.decorated_view(
                                self.http_cached_view(
                            self.http_cached_view(
                                self.decorated_view(
                                    self.rendered_view(
                                        self.mapped_view(view)))))))))
@@ -3000,11 +3140,15 @@
    @wraps_view
    def http_cached_view(self, view):
        if self.registry.settings.get('prevent_http_cache', False):
            return view
        seconds = self.kw.get('http_cache')
        options = {}
        if seconds is None:
            return view
        options = {}
        if isinstance(seconds, (tuple, list)):
            try:
@@ -3016,9 +3160,10 @@
        def wrapper(context, request):
            response = view(context, request)
            cache_expires = getattr(response, 'cache_expires', None)
            if cache_expires is not None:
                cache_expires(seconds, **options)
            prevent_caching = getattr(response.cache_control, 'prevent_auto',
                                      False)
            if not prevent_caching:
                response.cache_expires(seconds, **options)
            return response
        return wrapper
@@ -3026,7 +3171,7 @@
    @wraps_view
    def secured_view(self, view):
        permission = self.kw.get('permission')
        if permission == '__no_permission_required__':
        if permission == NO_PERMISSION_REQUIRED:
            # allow views registered within configurations that have a
            # default permission to explicitly override the default
            # permission, replacing it with no permission at all
@@ -3128,19 +3273,26 @@
    @wraps_view
    def rendered_view(self, view):
        wrapped_view = view
        static_renderer = self.kw.get('renderer')
        if static_renderer is None:
        # one way or another this wrapper must produce a Response (unless
        # the renderer is a NullRendererHelper)
        renderer = self.kw.get('renderer')
        if renderer is None:
            # register a default renderer if you want super-dynamic
            # rendering.  registering a default renderer will also allow
            # override_renderer to work if a renderer is left unspecified for
            # a view registration.
            return self._response_resolved_view(view)
        if renderer is renderers.null_renderer:
            return view
        return self._rendered_view(view, renderer)
        def _rendered_view(context, request):
            renderer = static_renderer
            result = wrapped_view(context, request)
            registry = self.kw['registry']
    def _rendered_view(self, view, view_renderer):
        def rendered_view(context, request):
            renderer = view_renderer
            result = view(context, request)
            registry = self.registry
            # this must adapt, it can't do a simple interface check
            # (avoid trying to render webob responses)
            response = registry.queryAdapterOrSelf(result, IResponse)
            if response is None:
                attrs = getattr(request, '__dict__', {})
@@ -3153,13 +3305,25 @@
                if '__view__' in attrs:
                    view_inst = attrs.pop('__view__')
                else:
                    view_inst = getattr(wrapped_view, '__original_view__',
                                        wrapped_view)
                    view_inst = getattr(view, '__original_view__', view)
                response = renderer.render_view(request, result, view_inst,
                                                context)
            return response
        return _rendered_view
        return rendered_view
    def _response_resolved_view(self, view):
        registry = self.registry
        def viewresult_to_response(context, request):
            result = view(context, request)
            response = registry.queryAdapterOrSelf(result, IResponse)
            if response is None:
                raise ValueError(
                    'Could not convert view return value "%s" into a '
                    'response object' % (result,))
            return response
        return viewresult_to_response
    @wraps_view
    def decorated_view(self, view):
@@ -3323,3 +3487,5 @@
        (inspect.isclass(o) and (issubclass(o, Exception)))
        )
global_registries = WeakOrderedSet()
pyramid/events.py
@@ -179,35 +179,48 @@
          event['mykey'] = 'foo'
    An object of this type is sent as an event just before a :term:`renderer`
    is invoked (but *after* the application-level renderer globals factory
    added via
    :class:`pyramid.config.Configurator.set_renderer_globals_factory`,
    if any, has injected its own keys into the renderer globals dictionary).
    is invoked (but *after* the -- deprecated -- application-level renderer
    globals factory added via
    :class:`pyramid.config.Configurator.set_renderer_globals_factory`, if
    any, has injected its own keys into the renderer globals dictionary).
    If a subscriber attempts to add a key that already exist in the renderer
    globals dictionary, a :exc:`KeyError` is raised.  This limitation is
    enforced because event subscribers do not possess any relative ordering.
    The set of keys added to the renderer globals dictionary by all
    :class:`pyramid.events.BeforeRender` subscribers and renderer globals
    factories must be unique.  """
    If a subscriber adds a key via ``__setitem__`` or that already exists in
    the renderer globals dictionary, it will overwrite an older value that is
    already in the globals dictionary.  This can be problematic because event
    subscribers to the BeforeRender event do not possess any relative
    ordering.  For maximum interoperability with other third-party
    subscribers, if you write an event subscriber meant to be used as a
    BeforeRender subscriber, your subscriber code will need to (using
    ``.get`` or ``__contains__`` of the event object) ensure no value already
    exists in the renderer globals dictionary before setting an overriding
    value.
    def __init__(self, system):
    The event has an additional attribute named ``rendering_val``.  This is
    the (non-system) value returned by a view or passed to ``render*`` as
    ``value``.  This feature is new in Pyramid 1.1.1.
    """
    def __init__(self, system, rendering_val=None):
        self._system = system
        self.rendering_val = rendering_val
    def __setitem__(self, name, value):
        """ Set a name/value pair into the dictionary which is passed to a
        renderer as the renderer globals dictionary.  If the ``name`` already
        exists in the target dictionary, a :exc:`KeyError` will be raised."""
        if name in self._system:
            raise KeyError('%s is already a renderer globals value' % name)
        renderer as the renderer globals dictionary."""
        self._system[name] = value
    def setdefault(self, name, default=None):
        """ Return the existing value for ``name`` in the renderers globals
        dictionary.  If no value with ``name`` exists in the dictionary, set
        the ``default`` value into the renderer globals dictionary under the
        name passed.  If a value already existed in the dictionary, return
        it.  If a value did not exist in the dictionary, return the default"""
        return self._system.setdefault(name, default)
    def update(self, d):
        """ Update the renderer globals dictionary with another dictionary
        ``d``.  If any of the key names in the source dictionary already exist
        in the target dictionary, a :exc:`KeyError` will be raised"""
        for k, v in d.items():
            self[k] = v
        ``d``."""
        return self._system.update(d)
    def __contains__(self, k):
        """ Return ``True`` if ``k`` exists in the renderer globals
@@ -223,4 +236,4 @@
        """ Return the value for key ``k`` from the renderer globals
        dictionary, or the default if no such value exists."""
        return self._system.get(k)
pyramid/httpexceptions.py
@@ -236,6 +236,11 @@
        if WSGIHTTPException.body_template_obj is not body_tmpl:
            # Custom template; add headers to args
            for k, v in environ.items():
                if (not k.startswith('wsgi.')) and ('.' in k):
                    # omit custom environ variables, stringifying them may
                    # trigger code that should not be executed here; see
                    # https://github.com/Pylons/pyramid/issues/239
                    continue
                args[k] = escape(v)
            for k, v in self.headers.items():
                args[k.lower()] = escape(v)
pyramid/i18n.py
@@ -214,6 +214,11 @@
        :param fileobj: the file-like object the translation should be read
                        from
        """
        # germanic plural by default; self.plural will be overwritten by
        # GNUTranslations._parse (called as a side effect if fileobj is
        # passed to GNUTranslations.__init__) with a "real" self.plural for
        # this domain; see https://github.com/Pylons/pyramid/issues/235
        self.plural = lambda n: int(n != 1)
        gettext.GNUTranslations.__init__(self, fp=fileobj)
        self.files = filter(None, [getattr(fileobj, 'name', None)])
        self.domain = domain
pyramid/interfaces.py
@@ -284,13 +284,18 @@
    """
    def __setitem__(name, value):
        """ Set a name/value pair into the dictionary which is passed to a
        renderer as the renderer globals dictionary.  If the ``name`` already
        exists in the target dictionary, a :exc:`KeyError` will be raised."""
        renderer as the renderer globals dictionary.  """
    def setdefault(name, default=None):
        """ Return the existing value for ``name`` in the renderers globals
        dictionary.  If no value with ``name`` exists in the dictionary, set
        the ``default`` value into the renderer globals dictionary under the
        name passed.  If a value already existed in the dictionary, return
        it.  If a value did not exist in the dictionary, return the default"""
    def update(d):
        """ Update the renderer globals dictionary with another dictionary
        ``d``.  If any of the key names in the source dictionary already exist
        in the target dictionary, a :exc:`KeyError` will be raised"""
        ``d``.  """
    def __contains__(k):
        """ Return ``True`` if ``k`` exists in the renderer globals
@@ -303,6 +308,10 @@
    def get(k, default=None):
        """ Return the value for key ``k`` from the renderer globals
        dictionary, or the default if no such value exists."""
    rendering_val = Attribute('The value returned by a view or passed to a '
                              '``render`` method for this rendering. '
                              'This feature is new in Pyramid 1.1.1.')
class IRenderer(Interface):
    def __call__(value, system):
@@ -436,6 +445,17 @@
class IRequest(Interface):
    """ Request type interface attached to all request objects """
class ITweens(Interface):
    """ Marker interface for utility registration representing the ordered
    set of a configuration's tween factories"""
class IRequestHandler(Interface):
    """ """
    def __call__(self, request):
        """ Must return a tuple of IReqest, IResponse or raise an exception.
        The ``request`` argument will be an instance of an object that
        provides IRequest."""
IRequest.combined = IRequest # for exception view lookups 
@@ -604,7 +624,7 @@
        'when this route matches (or ``None``)')
    predicates = Attribute(
        'A sequence of :term:`route predicate` objects used to '
        'determine if a request matches this route or not or not after '
        'determine if a request matches this route or not after '
        'basic pattern matching has been completed.')
    pregenerator = Attribute('This attribute should either be ``None`` or '
                             'a callable object implementing the '
@@ -750,13 +770,13 @@
        """ Pop a queue from the flash storage.  The queue is removed from
        flash storage after this message is called.  The queue is returned;
        it is a list of flash messages added by
        :meth:`pyramid.interfaces.ISesssion.flash`"""
        :meth:`pyramid.interfaces.ISession.flash`"""
    def peek_flash(queue=''):
        """ Peek at a queue in the flash storage.  The queue remains in
        flash storage after this message is called.  The queue is returned;
        it is a list of flash messages added by
        :meth:`pyramid.interfaces.ISesssion.flash`
        :meth:`pyramid.interfaces.ISession.flash`
        """
    def new_csrf_token():
@@ -846,8 +866,6 @@
    def __contains__(key):
        """Return true if a key exists in the mapping."""
NO_PERMISSION_REQUIRED = '__no_permission_required__'
class IRendererInfo(Interface):
    """ An object implementing this interface is passed to every
    :term:`renderer factory` constructor as its only argument (conventionally
@@ -861,4 +879,3 @@
    settings = Attribute('The deployment settings dictionary related '
                         'to the current application')
    
pyramid/log.py
File was deleted
pyramid/paster.py
@@ -1,3 +1,4 @@
import ConfigParser
import os
import sys
from code import interact
@@ -7,7 +8,14 @@
from paste.deploy import loadapp
from paste.script.command import Command
from pyramid.scripting import get_root
from pyramid.interfaces import IMultiView
from pyramid.interfaces import ITweens
from pyramid.scripting import prepare
from pyramid.util import DottedNameResolver
from pyramid.tweens import MAIN
from pyramid.tweens import INGRESS
from pyramid.scaffolds import PyramidTemplate # bw compat
zope.deprecation.deprecated(
@@ -15,22 +23,72 @@
                        'pyramid.scaffolds.PyramidTemplate in Pyramid 1.1'),
)
def get_app(config_file, name, loadapp=loadapp):
def get_app(config_uri, name=None, loadapp=loadapp):
    """ Return the WSGI application named ``name`` in the PasteDeploy
    config file ``config_file``"""
    config_name = 'config:%s' % config_file
    config file specified by ``config_uri``.
    If the ``name`` is None, this will attempt to parse the name from
    the ``config_uri`` string expecting the format ``inifile#name``.
    If no name is found, the name will default to "main"."""
    if '#' in config_uri:
        path, section = config_uri.split('#', 1)
    else:
        path, section = config_uri, 'main'
    if name:
        section = name
    config_name = 'config:%s' % path
    here_dir = os.getcwd()
    app = loadapp(config_name, name=name, relative_to=here_dir)
    app = loadapp(config_name, name=section, relative_to=here_dir)
    return app
_marker = object()
def bootstrap(config_uri, request=None):
    """ Load a WSGI application from the PasteDeploy config file specified
    by ``config_uri``. The environment will be configured as if it is
    currently serving ``request``, leaving a natural environment in place
    to write scripts that can generate URLs and utilize renderers.
    This function returns a dictionary with ``app``, ``root``, ``closer``,
    ``request``, and ``registry`` keys.  ``app`` is the WSGI app loaded
    (based on the ``config_uri``), ``root`` is the traversal root resource
    of the Pyramid application, and ``closer`` is a parameterless callback
    that may be called when your script is complete (it pops a threadlocal
    stack).
    .. note:: Most operations within :app:`Pyramid` expect to be invoked
              within the context of a WSGI request, thus it's important when
              loading your application to anchor it when executing scripts
              and other code that is not normally invoked during active WSGI
              requests.
    .. note:: For a complex config file containing multiple :app:`Pyramid`
              applications, this function will setup the environment under
              the context of the last-loaded :app:`Pyramid` application. You
              may load a specific application yourself by using the
              lower-level functions :meth:`pyramid.paster.get_app` and
              :meth:`pyramid.scripting.prepare` in conjunction with
              :attr:`pyramid.config.global_registries`.
    ``config_uri`` -- specifies the PasteDeploy config file to use for the
    interactive shell. The format is ``inifile#name``. If the name is left
    off, ``main`` will be assumed.
    ``request`` -- specified to anchor the script to a given set of WSGI
    parameters. For example, most people would want to specify the host,
    scheme and port such that their script will generate URLs in relation
    to those parameters. A request with default parameters is constructed
    for you if none is provided. You can mutate the request's ``environ``
    later to setup a specific host/port/scheme/etc.
    See :ref:`writing_a_script` for more information about how to use this
    function.
    """
    app = get_app(config_uri)
    env = prepare(request)
    env['app'] = app
    return env
class PCommand(Command):
    get_app = staticmethod(get_app) # hook point
    get_root = staticmethod(get_root) # hook point
    group_name = 'pyramid'
    interact = (interact,) # for testing
    loadapp = (loadapp,) # for testing
    bootstrap = (bootstrap,) # testing
    verbose = 3
    def __init__(self, *arg, **kw):
@@ -42,28 +100,27 @@
class PShellCommand(PCommand):
    """Open an interactive shell with a :app:`Pyramid` app loaded.
    This command accepts two positional arguments:
    This command accepts one positional argument:
    ``config_file`` -- specifies the PasteDeploy config file to use
    for the interactive shell.
    ``section_name`` -- specifies the section name in the PasteDeploy
    config file that represents the application.
    ``config_uri`` -- specifies the PasteDeploy config file to use for the
    interactive shell. The format is ``inifile#name``. If the name is left
    off, ``main`` will be assumed.
    Example::
        $ paster pshell myapp.ini main
        $ paster pshell myapp.ini#main
    .. note:: You should use a ``section_name`` that refers to the
              actual ``app`` section in the config file that points at
              your Pyramid app without any middleware wrapping, or this
              command will almost certainly fail.
    .. note:: If you do not point the loader directly at the section of the
              ini file containing your :app:`Pyramid` application, the
              command will attempt to find the app for you. If you are
              loading a pipeline that contains more than one :app:`Pyramid`
              application within it, the loader will use the last one.
    """
    summary = "Open an interactive shell with a Pyramid application loaded"
    min_args = 2
    max_args = 2
    min_args = 1
    max_args = 1
    parser = Command.standard_parser(simulate=True)
    parser.add_option('-d', '--disable-ipython',
@@ -71,34 +128,109 @@
                      dest='disable_ipython',
                      help="Don't use IPython even if it is available")
    def command(self, IPShell=_marker):
        # IPShell passed to command method is for testing purposes
        if IPShell is _marker: # pragma: no cover
            try:
                from IPython.Shell import IPShell
            except ImportError:
                IPShell = None
        cprt =('Type "help" for more information. "root" is the Pyramid app '
               'root object, "registry" is the Pyramid registry object.')
        banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt)
        config_file, section_name = self.args
        self.logging_file_config(config_file)
        app = self.get_app(config_file, section_name, loadapp=self.loadapp[0])
        root, closer = self.get_root(app)
        shell_globals = {'root':root, 'registry':app.registry}
    ConfigParser = ConfigParser.ConfigParser # testing
        if (IPShell is None) or self.options.disable_ipython:
    def pshell_file_config(self, filename):
        resolver = DottedNameResolver(None)
        self.loaded_objects = {}
        self.object_help = {}
        config = self.ConfigParser()
        config.read(filename)
        try:
            items = config.items('pshell')
        except ConfigParser.NoSectionError:
            return
        for k, v in items:
            self.loaded_objects[k] = resolver.maybe_resolve(v)
            self.object_help[k] = v
    def command(self, shell=None):
        config_uri = self.args[0]
        config_file = config_uri.split('#', 1)[0]
        self.logging_file_config(config_file)
        self.pshell_file_config(config_file)
        # bootstrap the environ
        env = self.bootstrap[0](config_uri)
        # remove the closer from the env
        closer = env.pop('closer')
        # setup help text for default environment
        env_help = dict(env)
        env_help['app'] = 'The WSGI application.'
        env_help['root'] = 'Root of the default resource tree.'
        env_help['registry'] = 'Active Pyramid registry.'
        env_help['request'] = 'Active request object.'
        env_help['root_factory'] = (
            'Default root factory used to create `root`.')
        # load the pshell section of the ini file
        env.update(self.loaded_objects)
        # eliminate duplicates from env, allowing custom vars to override
        for k in self.loaded_objects:
            if k in env_help:
                del env_help[k]
        # generate help text
        help = '\n'
        if env_help:
            help += 'Environment:'
            for var in sorted(env_help.keys()):
                help += '\n  %-12s %s' % (var, env_help[var])
        if self.object_help:
            help += '\n\nCustom Variables:'
            for var in sorted(self.object_help.keys()):
                help += '\n  %-12s %s' % (var, self.object_help[var])
        if shell is None and not self.options.disable_ipython:
            shell = self.make_ipython_v0_11_shell()
            if shell is None:
                shell = self.make_ipython_v0_10_shell()
        if shell is None:
            shell = self.make_default_shell()
        try:
            shell(env, help)
        finally:
            closer()
    def make_default_shell(self, interact=interact):
        def shell(env, help):
            cprt = 'Type "help" for more information.'
            banner = "Python %s on %s\n%s" % (sys.version, sys.platform, cprt)
            banner += '\n' + help + '\n'
            interact(banner, local=env)
        return shell
    def make_ipython_v0_11_shell(self, IPShellFactory=None):
        if IPShellFactory is None: # pragma: no cover
            try:
                self.interact[0](banner, local=shell_globals)
            finally:
                closer()
        else:
                from IPython.frontend.terminal.embed import (
                    InteractiveShellEmbed)
                IPShellFactory = InteractiveShellEmbed
            except ImportError:
                return None
        def shell(env, help):
            IPShell = IPShellFactory(banner2=help, user_ns=env)
            IPShell()
        return shell
    def make_ipython_v0_10_shell(self, IPShellFactory=None):
        if IPShellFactory is None: # pragma: no cover
            try:
                shell = IPShell(argv=[], user_ns=shell_globals)
                shell.IP.BANNER = shell.IP.BANNER + '\n\n' + banner
                shell.mainloop()
            finally:
                closer()
                from IPython.Shell import IPShellEmbed
                IPShellFactory = IPShellEmbed
            except ImportError:
                return None
        def shell(env, help):
            IPShell = IPShellFactory(argv=[], user_ns=env)
            IPShell.set_banner(IPShell.IP.BANNER + '\n' + help + '\n')
            IPShell()
        return shell
BFGShellCommand = PShellCommand # b/w compat forever
@@ -108,33 +240,26 @@
    route, the pattern of the route, and the view callable which will be
    invoked when the route is matched.
    This command accepts two positional arguments:
    This command accepts one positional argument:
    ``config_file`` -- specifies the PasteDeploy config file to use
    for the interactive shell.
    ``section_name`` -- specifies the section name in the PasteDeploy
    config file that represents the application.
    ``config_uri`` -- specifies the PasteDeploy config file to use for the
    interactive shell. The format is ``inifile#name``. If the name is left
    off, ``main`` will be assumed.
    Example::
        $ paster proutes myapp.ini main
        $ paster proutes myapp.ini#main
    .. note:: You should use a ``section_name`` that refers to the
              actual ``app`` section in the config file that points at
              your Pyramid app without any middleware wrapping, or this
              command will almost certainly fail.
    """
    summary = "Print all URL dispatch routes related to a Pyramid application"
    min_args = 2
    max_args = 2
    min_args = 1
    max_args = 1
    stdout = sys.stdout
    parser = Command.standard_parser(simulate=True)
    def _get_mapper(self, app):
    def _get_mapper(self, registry):
        from pyramid.config import Configurator
        registry = app.registry
        config = Configurator(registry = registry)
        return config.get_routes_mapper()
@@ -146,10 +271,10 @@
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IView
        from zope.interface import Interface
        config_file, section_name = self.args
        app = self.get_app(config_file, section_name, loadapp=self.loadapp[0])
        registry = app.registry
        mapper = self._get_mapper(app)
        config_uri = self.args[0]
        env = self.bootstrap[0](config_uri)
        registry = env['registry']
        mapper = self._get_mapper(registry)
        if mapper is not None:
            routes = mapper.get_routes()
            fmt = '%-15s %-30s %-25s'
@@ -171,36 +296,28 @@
                    self.out(fmt % (route.name, route.pattern, view_callable))
from pyramid.interfaces import IMultiView
class PViewsCommand(PCommand):
    """Print, for a given URL, the views that might match. Underneath each
    potentially matching route, list the predicates required. Underneath
    each route+predicate set, print each view that might match and its
    predicates.
    This command accepts three positional arguments:
    This command accepts two positional arguments:
    ``config_file`` -- specifies the PasteDeploy config file to use
    for the interactive shell.
    ``section_name`` -- specifies the section name in the PasteDeploy
    config file that represents the application.
    ``config_uri`` -- specifies the PasteDeploy config file to use for the
    interactive shell. The format is ``inifile#name``. If the name is left
    off, ``main`` will be assumed.
    ``url`` -- specifies the URL that will be used to find matching views.
    Example::
        $ paster proutes myapp.ini main url
        $ paster proutes myapp.ini#main url
    .. note:: You should use a ``section_name`` that refers to the
              actual ``app`` section in the config file that points at
              your Pyramid app without any middleware wrapping, or this
              command will almost certainly fail.
    """
    summary = "Print all views in an application that might match a URL"
    min_args = 3
    max_args = 3
    min_args = 2
    max_args = 2
    stdout = sys.stdout
    parser = Command.standard_parser(simulate=True)
@@ -395,11 +512,11 @@
                self.out("%sview predicates (%s)" % (indent, predicate_text))
    def command(self):
        config_file, section_name, url = self.args
        config_uri, url = self.args
        if not url.startswith('/'):
            url = '/%s' % url
        app = self.get_app(config_file, section_name, loadapp=self.loadapp[0])
        registry = app.registry
        env = self.bootstrap[0](config_uri)
        registry = env['registry']
        view = self._find_view(url, registry)
        self.out('')
        self.out("URL = %s" % url)
@@ -420,3 +537,84 @@
                self.out("    Not found.")
        self.out('')
class PTweensCommand(PCommand):
    """Print all implicit and explicit :term:`tween` objects used by a
    Pyramid application.  The handler output includes whether the system is
    using an explicit tweens ordering (will be true when the
    ``pyramid.tweens`` setting is used) or an implicit tweens ordering (will
    be true when the ``pyramid.tweens`` setting is *not* used).
    This command accepts one positional argument:
    ``config_uri`` -- specifies the PasteDeploy config file to use for the
    interactive shell. The format is ``inifile#name``. If the name is left
    off, ``main`` will be assumed.
    Example::
        $ paster ptweens myapp.ini#main
    """
    summary = "Print all tweens related to a Pyramid application"
    min_args = 1
    max_args = 1
    stdout = sys.stdout
    parser = Command.standard_parser(simulate=True)
    def _get_tweens(self, registry):
        from pyramid.config import Configurator
        config = Configurator(registry = registry)
        return config.registry.queryUtility(ITweens)
    def out(self, msg): # pragma: no cover
        print msg
    def show_implicit(self, tweens):
        implicit = tweens.implicit()
        fmt = '%-10s  %-50s  %-15s'
        self.out(fmt % ('Position', 'Name', 'Alias'))
        self.out(fmt % (
            '-'*len('Position'), '-'*len('Name'), '-'*len('Alias')))
        self.out(fmt % ('-', '-', INGRESS))
        for pos, (name, _) in enumerate(implicit):
            alias = tweens.name_to_alias.get(name, None)
            self.out(fmt % (pos, name, alias))
        self.out(fmt % ('-', '-', MAIN))
    def show_explicit(self, tweens):
        explicit = tweens.explicit
        fmt = '%-10s  %-65s'
        self.out(fmt % ('Position', 'Name'))
        self.out(fmt % ('-'*len('Position'), '-'*len('Name')))
        self.out(fmt % ('-', INGRESS))
        for pos, (name, _) in enumerate(explicit):
            self.out(fmt % (pos, name))
        self.out(fmt % ('-', MAIN))
    def command(self):
        config_uri = self.args[0]
        env = self.bootstrap[0](config_uri)
        registry = env['registry']
        tweens = self._get_tweens(registry)
        if tweens is not None:
            explicit = tweens.explicit
            if explicit:
                self.out('"pyramid.tweens" config value set '
                         '(explicitly ordered tweens used)')
                self.out('')
                self.out('Explicit Tween Chain (used)')
                self.out('')
                self.show_explicit(tweens)
                self.out('')
                self.out('Implicit Tween Chain (not used)')
                self.out('')
                self.show_implicit(tweens)
            else:
                self.out('"pyramid.tweens" config value NOT set '
                         '(implicitly ordered tweens used)')
                self.out('')
                self.out('Implicit Tween Chain')
                self.out('')
                self.show_implicit(tweens)
pyramid/renderers.py
@@ -3,6 +3,7 @@
import threading
from zope.interface import implements
from zope.deprecation import deprecated
from pyramid.interfaces import IChameleonLookup
from pyramid.interfaces import IChameleonTranslate
@@ -97,7 +98,9 @@
    Supply a ``request`` parameter in order to provide the renderer
    with the most correct 'system' values (``request`` and ``context``
    in particular).
    in particular). Keep in mind that if the ``request`` parameter is
    not passed in, any changes to ``request.response`` attributes made
    before calling this function will be ignored.
    """
    try:
@@ -335,9 +338,15 @@
            lock.release()
    return lookup(info)
# XXX deprecate
def renderer_from_name(path, package=None):
    return RendererHelper(name=path, package=package).renderer
deprecated(
    'renderer_from_name',
    'The "pyramid.renderers.renderer_from_name" function was never an API. '
    'However, its use has been observed "in the wild."  It will disappear in '
    'the next major release. To replace it, use the '
    '``pyramid.renderers.get_renderer`` API instead. ')
class RendererHelper(object):
    implements(IRendererInfo)
@@ -345,7 +354,8 @@
        if name and '.' in name:
            rtype = os.path.splitext(name)[1]
        else:
            rtype = name
            # important.. must be a string; cannot be None; see issue 249
            rtype = name or ''
        if registry is None:
            registry = get_current_registry()
@@ -402,7 +412,7 @@
            if renderer_globals:
                system_values.update(renderer_globals)
        registry.notify(BeforeRender(system_values))
        registry.notify(BeforeRender(system_values, value))
        result = renderer(value, system_values)
        return result
@@ -412,6 +422,8 @@
        return self._make_response(result, request)
    def _make_response(self, result, request):
        # broken out of render_to_response as a separate method for testing
        # purposes
        response = getattr(request, 'response', None)
        if response is None:
            # request is None or request is not a pyramid.response.Response
@@ -451,3 +463,44 @@
                response.cache_expires = cache_for
        return response
    def clone(self, name=None, package=None, registry=None):
        if name is None:
            name = self.name
        if package is None:
            package = self.package
        if registry is None:
            registry = self.registry
        return self.__class__(name=name, package=package, registry=registry)
class NullRendererHelper(RendererHelper):
    """ Special renderer helper that has render_* methods which simply return
    the value they are fed rather than converting them to response objects;
    useful for testing purposes and special case view configuration
    registrations that want to use the view configuration machinery but do
    not want actual rendering to happen ."""
    def __init__(self, name=None, package=None, registry=None):
        # we override the initializer to avoid calling get_current_registry
        # (it will return a reference to the global registry when this
        # thing is called at module scope; we don't want that).
        self.name = None
        self.package = None
        self.type = ''
        self.registry = None
    @property
    def settings(self):
        return get_current_registry().settings or {}
    def render_view(self, request, value, view, context):
        return value
    def render(self, value, system_values, request=None):
        return value
    def render_to_response(self, value, system_values, request=None):
        return value
    def clone(self, name=None, package=None, registry=None):
        return self
null_renderer = NullRendererHelper()
pyramid/request.py
@@ -204,6 +204,7 @@
    response_callbacks = ()
    finished_callbacks = ()
    exception = None
    exc_info = None
    matchdict = None
    matched_route = None
@@ -491,16 +492,24 @@
        return adapted is ob
    @property
    def json(self):
        if self.content_type == 'application/json':
            return json.loads(self.body, encoding=self.charset)
    def json_body(self):
        return json.loads(self.body, encoding=self.charset)
def route_request_iface(name, bases=()):
    iface = InterfaceClass('%s_IRequest' % name, bases=bases)
    # zope.interface treats the __name__ as the __doc__ and changes __name__
    # to None for interfaces that contain spaces if you do not pass a
    # nonempty __doc__ (insane); see
    # zope.interface.interface.Element.__init__ and
    # https://github.com/Pylons/pyramid/issues/232; as a result, always pass
    # __doc__ to the InterfaceClass constructor.
    iface = InterfaceClass('%s_IRequest' % name, bases=bases,
                           __doc__="route_request_iface-generated interface")
    # for exception view lookups
    iface.combined = InterfaceClass('%s_combined_IRequest' % name,
                                    bases=(iface, IRequest))
    iface.combined = InterfaceClass(
        '%s_combined_IRequest' % name,
        bases=(iface, IRequest),
        __doc__ = 'route_request_iface-generated combined interface')
    return iface
def add_global_response_headers(request, headerlist):
pyramid/response.py
@@ -1,3 +1,5 @@
import venusian
from webob import Response as _Response
from zope.interface import implements
from pyramid.interfaces import IResponse
@@ -5,3 +7,60 @@
class Response(_Response):
    implements(IResponse)
class response_adapter(object):
    """ Decorator activated via a :term:`scan` which treats the function
    being decorated as a :term:`response adapter` for the set of types or
    interfaces passed as ``*types_or_ifaces`` to the decorator constructor.
    For example, if you scan the following response adapter:
    .. code-block:: python
        from pyramid.response import Response
        from pyramid.response import response_adapter
        @response_adapter(int)
        def myadapter(i):
            return Response(status=i)
    You can then return an integer from your view callables, and it will be
    converted into a response with the integer as the status code.
    More than one type or interface can be passed as a constructor argument.
    The decorated response adapter will be called for each type or interface.
    .. code-block:: python
        import json
        from pyramid.response import Response
        from pyramid.response import response_adapter
        @response_adapter(dict, list)
        def myadapter(ob):
            return Response(json.dumps(ob))
    This method will have no effect until a :term:`scan` is performed
    agains the package or module which contains it, ala:
    .. code-block:: python
        from pyramid.config import Configurator
        config = Configurator()
        config.scan('somepackage_containing_adapters')
    """
    venusian = venusian # for unit testing
    def __init__(self, *types_or_ifaces):
        self.types_or_ifaces = types_or_ifaces
    def register(self, scanner, name, wrapped):
        config = scanner.config
        for type_or_iface in self.types_or_ifaces:
            config.add_response_adapter(wrapped, type_or_iface)
    def __call__(self, wrapped):
        self.venusian.attach(wrapped, self.register, category='pyramid')
        return wrapped
pyramid/router.py
@@ -2,7 +2,6 @@
from zope.interface import providedBy
from pyramid.interfaces import IDebugLogger
from pyramid.interfaces import IExceptionViewClassifier
from pyramid.interfaces import IRequest
from pyramid.interfaces import IRootFactory
from pyramid.interfaces import IRouteRequest
@@ -12,7 +11,7 @@
from pyramid.interfaces import ITraverser
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IResponse
from pyramid.interfaces import ITweens
from pyramid.events import ContextFound
from pyramid.events import NewRequest
@@ -22,6 +21,7 @@
from pyramid.threadlocal import manager
from pyramid.traversal import DefaultRootFactory
from pyramid.traversal import ResourceTreeTraverser
from pyramid.tweens import excview_tween_factory
class Router(object):
    implements(IRouter)
@@ -37,13 +37,122 @@
        self.root_factory = q(IRootFactory, default=DefaultRootFactory)
        self.routes_mapper = q(IRoutesMapper)
        self.request_factory = q(IRequestFactory, default=Request)
        tweens = q(ITweens)
        if tweens is None:
            tweens = excview_tween_factory
        self.handle_request = tweens(self.handle_request, registry)
        self.root_policy = self.root_factory # b/w compat
        self.registry = registry
        settings = registry.settings
        if settings is not None:
            self.debug_notfound = settings['debug_notfound']
            self.debug_routematch = settings['debug_routematch']
    def handle_request(self, request):
        attrs = request.__dict__
        registry = attrs['registry']
        request.request_iface = IRequest
        context = None
        routes_mapper = self.routes_mapper
        debug_routematch = self.debug_routematch
        adapters = registry.adapters
        has_listeners = registry.has_listeners
        notify = registry.notify
        logger = self.logger
        has_listeners and notify(NewRequest(request))
        # find the root object
        root_factory = self.root_factory
        if routes_mapper is not None:
            info = routes_mapper(request)
            match, route = info['match'], info['route']
            if route is None:
                if debug_routematch:
                    msg = ('no route matched for url %s' %
                           request.url)
                    logger and logger.debug(msg)
            else:
                # TODO: kill off bfg.routes.* environ keys
                # when traverser requires request arg, and
                # cant cope with environ anymore (they are
                # docs-deprecated as of BFG 1.3)
                environ = request.environ
                environ['bfg.routes.route'] = route
                environ['bfg.routes.matchdict'] = match
                attrs['matchdict'] = match
                attrs['matched_route'] = route
                if debug_routematch:
                    msg = (
                        'route matched for url %s; '
                        'route_name: %r, '
                        'path_info: %r, '
                        'pattern: %r, '
                        'matchdict: %r, '
                        'predicates: %r' % (
                            request.url,
                            route.name,
                            request.path_info,
                            route.pattern, match,
                            route.predicates)
                        )
                    logger and logger.debug(msg)
                request.request_iface = registry.queryUtility(
                    IRouteRequest,
                    name=route.name,
                    default=IRequest)
                root_factory = route.factory or self.root_factory
        root = root_factory(request)
        attrs['root'] = root
        # find a context
        traverser = adapters.queryAdapter(root, ITraverser)
        if traverser is None:
            traverser = ResourceTreeTraverser(root)
        tdict = traverser(request)
        context, view_name, subpath, traversed, vroot, vroot_path = (
            tdict['context'],
            tdict['view_name'],
            tdict['subpath'],
            tdict['traversed'],
            tdict['virtual_root'],
            tdict['virtual_root_path']
            )
        attrs.update(tdict)
        has_listeners and notify(ContextFound(request))
        # find a view callable
        context_iface = providedBy(context)
        view_callable = adapters.lookup(
            (IViewClassifier, request.request_iface, context_iface),
            IView, name=view_name, default=None)
        # invoke the view callable
        if view_callable is None:
            if self.debug_notfound:
                msg = (
                    'debug_notfound of url %s; path_info: %r, '
                    'context: %r, view_name: %r, subpath: %r, '
                    'traversed: %r, root: %r, vroot: %r, '
                    'vroot_path: %r' % (
                        request.url, request.path_info, context,
                        view_name, subpath, traversed, root, vroot,
                        vroot_path)
                    )
                logger and logger.debug(msg)
            else:
                msg = request.path_info
            raise HTTPNotFound(msg)
        else:
            response = view_callable(context, request)
        return response
    def __call__(self, environ, start_response):
        """
@@ -54,144 +163,27 @@
        return an iterable.
        """
        registry = self.registry
        adapters = registry.adapters
        has_listeners = registry.has_listeners
        notify = registry.notify
        logger = self.logger
        manager = self.threadlocal_manager
        routes_mapper = self.routes_mapper
        debug_routematch = self.debug_routematch
        request = None
        has_listeners = self.registry.has_listeners
        notify = self.registry.notify
        request = self.request_factory(environ)
        threadlocals = {'registry':registry, 'request':request}
        manager = self.threadlocal_manager
        manager.push(threadlocals)
        request.registry = registry
        try:
        try: # matches finally: manager.pop()
            try: # matches finally:  ... call request finished callbacks ...
                # create the request
                request = self.request_factory(environ)
                context = None
                threadlocals['request'] = request
                attrs = request.__dict__
                attrs['registry'] = registry
                request_iface = IRequest
                try: # matches except Exception (exception view execution)
                    has_listeners and notify(NewRequest(request))
                    # find the root object
                    root_factory = self.root_factory
                    if routes_mapper is not None:
                        info = routes_mapper(request)
                        match, route = info['match'], info['route']
                        if route is None:
                            if debug_routematch:
                                msg = ('no route matched for url %s' %
                                       request.url)
                                logger and logger.debug(msg)
                        else:
                            # TODO: kill off bfg.routes.* environ keys when
                            # traverser requires request arg, and cant cope
                            # with environ anymore (they are docs-deprecated as
                            # of BFG 1.3)
                            environ['bfg.routes.route'] = route
                            environ['bfg.routes.matchdict'] = match
                            attrs['matchdict'] = match
                            attrs['matched_route'] = route
                            if debug_routematch:
                                msg = (
                                    'route matched for url %s; '
                                    'route_name: %r, '
                                    'path_info: %r, '
                                    'pattern: %r, '
                                    'matchdict: %r, '
                                    'predicates: %r' % (
                                        request.url,
                                        route.name,
                                        request.path_info,
                                        route.pattern, match,
                                        route.predicates)
                                    )
                                logger and logger.debug(msg)
                            request_iface = registry.queryUtility(
                                IRouteRequest,
                                name=route.name,
                                default=IRequest)
                            root_factory = route.factory or self.root_factory
                    root = root_factory(request)
                    attrs['root'] = root
                    # find a context
                    traverser = adapters.queryAdapter(root, ITraverser)
                    if traverser is None:
                        traverser = ResourceTreeTraverser(root)
                    tdict = traverser(request)
                    context, view_name, subpath, traversed, vroot, vroot_path =(
                        tdict['context'], tdict['view_name'], tdict['subpath'],
                        tdict['traversed'], tdict['virtual_root'],
                        tdict['virtual_root_path'])
                    attrs.update(tdict)
                    has_listeners and notify(ContextFound(request))
                    # find a view callable
                    context_iface = providedBy(context)
                    view_callable = adapters.lookup(
                        (IViewClassifier, request_iface, context_iface),
                        IView, name=view_name, default=None)
                    # invoke the view callable
                    if view_callable is None:
                        if self.debug_notfound:
                            msg = (
                                'debug_notfound of url %s; path_info: %r, '
                                'context: %r, view_name: %r, subpath: %r, '
                                'traversed: %r, root: %r, vroot: %r, '
                                'vroot_path: %r' % (
                                    request.url, request.path_info, context,
                                    view_name,
                                    subpath, traversed, root, vroot, vroot_path)
                                )
                            logger and logger.debug(msg)
                        else:
                            msg = request.path_info
                        raise HTTPNotFound(msg)
                    else:
                        result = view_callable(context, request)
                # handle exceptions raised during root finding and view-exec
                except Exception, why:
                    attrs['exception'] = why
                    for_ = (IExceptionViewClassifier,
                            request_iface.combined,
                            providedBy(why))
                    view_callable = adapters.lookup(for_, IView, default=None)
                    if view_callable is None:
                        raise
                    result = view_callable(why, request)
                # process the response
                response = registry.queryAdapterOrSelf(result, IResponse)
                if response is None:
                    raise ValueError(
                        'Could not convert view return value "%s" into a '
                        'response object' % (result,))
            try:
                response = self.handle_request(request)
                has_listeners and notify(NewResponse(request, response))
                if request.response_callbacks:
                    request._process_response_callbacks(response)
                return response(request.environ, start_response)
            finally:
                if request is not None and request.finished_callbacks:
                if request.finished_callbacks:
                    request._process_finished_callbacks()
            return response(request.environ, start_response)
        finally:
            manager.pop()
pyramid/scaffolds/alchemy/+package+/models.py
@@ -76,7 +76,7 @@
    try:
        populate()
    except IntegrityError:
        DBSession.rollback()
        transaction.abort()
    return DBSession
def appmaker(engine):
pyramid/scaffolds/alchemy/+package+/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
pyramid/scaffolds/alchemy/+package+/templates/model.pt_tmpl
@@ -7,8 +7,8 @@
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
  <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen">
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen">
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -32,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -50,22 +50,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
pyramid/scaffolds/alchemy/+package+/templates/root.pt_tmpl
@@ -7,8 +7,8 @@
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
  <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen">
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen">
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -32,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -48,22 +48,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
pyramid/scaffolds/alchemy/development.ini_tmpl
@@ -1,22 +1,20 @@
[app:{{project}}]
use = egg:{{project}}
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
pyramid.include = pyramid_debugtoolbar
                  pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/{{project}}.db
[pipeline:main]
pipeline =
    egg:WebError#evalerror
    tm
    {{project}}
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[server:main]
use = egg:Paste#http
pyramid/scaffolds/alchemy/production.ini_tmpl
@@ -1,11 +1,14 @@
[app:{{project}}]
use = egg:{{project}}
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
pyramid.includes = pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/{{project}}.db
[filter:weberror]
@@ -22,14 +25,9 @@
;smtp_use_tls =
;error_message =
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[pipeline:main]
pipeline =
    weberror
    tm
    {{project}}
[server:main]
pyramid/scaffolds/alchemy/setup.py_tmpl
@@ -9,7 +9,8 @@
requires = [
    'pyramid',
    'repoze.tm2>=1.0b1', # default_commit_veto
    'pyramid_tm',
    'pyramid_debugtoolbar',
    'sqlalchemy',
    'zope.sqlalchemy',
    'WebError',
pyramid/scaffolds/routesalchemy/+package+/models.py
@@ -39,4 +39,4 @@
    try:
        populate()
    except IntegrityError:
        DBSession.rollback()
        transaction.abort()
pyramid/scaffolds/routesalchemy/+package+/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
pyramid/scaffolds/routesalchemy/+package+/templates/mytemplate.pt_tmpl
@@ -7,8 +7,8 @@
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
  <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen">
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen">
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -32,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -44,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
pyramid/scaffolds/routesalchemy/development.ini_tmpl
@@ -1,22 +1,20 @@
[app:{{project}}]
use = egg:{{project}}
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
pyramid.include = pyramid_debugtoolbar
                  pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/{{project}}.db
[pipeline:main]
pipeline =
    egg:WebError#evalerror
    tm
    {{project}}
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[server:main]
use = egg:Paste#http
pyramid/scaffolds/routesalchemy/production.ini_tmpl
@@ -1,11 +1,14 @@
[app:{{project}}]
use = egg:{{project}}
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
pyramid.include = pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/{{project}}.db
[filter:weberror]
@@ -22,14 +25,9 @@
;smtp_use_tls =
;error_message =
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[pipeline:main]
pipeline =
    weberror
    tm
    {{project}}
[server:main]
pyramid/scaffolds/routesalchemy/setup.py_tmpl
@@ -11,7 +11,8 @@
    'pyramid',
    'SQLAlchemy',
    'transaction',
    'repoze.tm2>=1.0b1', # default_commit_veto
    'pyramid_tm',
    'pyramid_debugtoolbar',
    'zope.sqlalchemy',
    'WebError',
    ]
pyramid/scaffolds/starter/+package+/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl
@@ -7,8 +7,8 @@
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
  <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen">
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen">
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -32,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -44,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
pyramid/scaffolds/starter/development.ini_tmpl
@@ -1,15 +1,16 @@
[app:{{project}}]
use = egg:{{project}}
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
pyramid.include = pyramid_debugtoolbar
[pipeline:main]
pipeline =
    egg:WebError#evalerror
    {{project}}
[server:main]
pyramid/scaffolds/starter/production.ini_tmpl
@@ -1,11 +1,12 @@
[app:{{project}}]
use = egg:{{project}}
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
[filter:weberror]
use = egg:WebError#error_catcher
pyramid/scaffolds/starter/setup.py_tmpl
@@ -6,7 +6,7 @@
README = open(os.path.join(here, 'README.txt')).read()
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = ['pyramid', 'WebError']
requires = ['pyramid', 'pyramid_debugtoolbar', 'WebError']
setup(name='{{project}}',
      version='0.0',
pyramid/scaffolds/tests.py
@@ -30,7 +30,6 @@
                [os.path.join(self.directory, 'bin', 'python'),
                 'setup.py', 'develop'])
            os.chdir(self.directory)
            subprocess.check_call(['bin/paster', 'create', '-t', tmpl_name,
                                   'Dingle'])
            os.chdir('Dingle')
@@ -38,23 +37,31 @@
            subprocess.check_call([py, 'setup.py', 'install'])
            subprocess.check_call([py, 'setup.py', 'test'])
            paster = os.path.join(self.directory, 'bin', 'paster')
            proc = subprocess.Popen([paster, 'serve', 'development.ini'])
            try:
                time.sleep(5)
                proc.poll()
                if proc.returncode is not None:
                    raise RuntimeError('didnt start')
                conn = httplib.HTTPConnection('localhost:6543')
                conn.request('GET', '/')
                resp = conn.getresponse()
                assert(resp.status == 200)
            finally:
                if hasattr(proc, 'terminate'):
                    # 2.6+
                    proc.terminate()
                else:
                    # 2.5
                    os.kill(proc.pid, signal.SIGTERM)
            for ininame, hastoolbar in (('development.ini', True),
                                        ('production.ini', False)):
                proc = subprocess.Popen([paster, 'serve', ininame])
                try:
                    time.sleep(5)
                    proc.poll()
                    if proc.returncode is not None:
                        raise RuntimeError('%s didnt start' % ininame)
                    conn = httplib.HTTPConnection('localhost:6543')
                    conn.request('GET', '/')
                    resp = conn.getresponse()
                    assert resp.status == 200, ininame
                    data = resp.read()
                    toolbarchunk = '<div id="flDebug"'
                    if hastoolbar:
                        assert toolbarchunk in data, ininame
                    else:
                        assert not toolbarchunk in data, ininame
                finally:
                    if hasattr(proc, 'terminate'):
                        # 2.6+
                        proc.terminate()
                    else:
                        # 2.5
                        os.kill(proc.pid, signal.SIGTERM)
        finally:
            shutil.rmtree(self.directory)
            os.chdir(self.old_cwd)
pyramid/scaffolds/zodb/+package+/static/pylons.css
@@ -23,7 +23,7 @@
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl
@@ -7,8 +7,8 @@
  <meta name="description" content="pyramid web application" />
  <link rel="shortcut icon" href="${request.static_url('{{package}}:static/favicon.ico')}" />
  <link rel="stylesheet" href="${request.static_url('{{package}}:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen">
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen">
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
  <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
  <!--[if lte IE 6]>
  <link rel="stylesheet" href="${request.static_url('{{package}}:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
  <![endif]-->
@@ -32,7 +32,7 @@
      <div class="bottom">
        <div id="left" class="align-right">
          <h2>Search documentation</h2>
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/dev/search.html">
          <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
                <input type="text" id="q" name="q" value="" />
                <input type="submit" id="x" value="Go" />
            </form>
@@ -44,22 +44,22 @@
              <a href="http://pylonsproject.org">Pylons Website</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#api-documentation">API Documentation</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#tutorials">Tutorials</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#change-history">Change History</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#sample-applications">Sample Applications</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
            </li>
            <li>
              <a href="http://docs.pylonsproject.org/projects/pyramid/dev/#support-and-development">Support and Development</a>
              <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
            </li>
            <li>
              <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
pyramid/scaffolds/zodb/development.ini_tmpl
@@ -1,24 +1,22 @@
[app:{{project}}]
use = egg:{{project}}
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = true
default_locale_name = en
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = true
pyramid.default_locale_name = en
pyramid.include = pyramid_debugtoolbar
                  pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[pipeline:main]
pipeline =
    egg:WebError#evalerror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    {{project}}
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[server:main]
use = egg:Paste#http
pyramid/scaffolds/zodb/production.ini_tmpl
@@ -1,11 +1,15 @@
[app:{{project}}]
use = egg:{{project}}
reload_templates = false
debug_authorization = false
debug_notfound = false
debug_routematch = false
debug_templates = false
default_locale_name = en
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.debug_templates = false
pyramid.default_locale_name = en
pyramid.include = pyramid_tm
pyramid_tm.attempts = 3
zodb_uri = file://%(here)s/Data.fs?connection_cache_size=20000
[filter:weberror]
@@ -22,16 +26,10 @@
;smtp_use_tls =
;error_message =
[filter:tm]
use = egg:repoze.tm2#tm
commit_veto = repoze.tm:default_commit_veto
[pipeline:main]
pipeline =
    weberror
    egg:repoze.zodbconn#closer
    egg:repoze.retry#retry
    tm
    {{project}}
[server:main]
pyramid/scaffolds/zodb/setup.py_tmpl
@@ -9,8 +9,8 @@
requires = [
    'pyramid',
    'repoze.zodbconn',
    'repoze.tm2>=1.0b1', # default_commit_veto
    'repoze.retry',
    'pyramid_tm',
    'pyramid_debugtoolbar',
    'ZODB3',
    'WebError',
    ]
pyramid/scripting.py
@@ -1,21 +1,26 @@
from pyramid.config import global_registries
from pyramid.exceptions import ConfigurationError
from pyramid.request import Request
from pyramid.interfaces import IRequestFactory
from pyramid.interfaces import IRootFactory
from pyramid.threadlocal import manager as threadlocal_manager
from pyramid.traversal import DefaultRootFactory
def get_root(app, request=None):
    """ Return a tuple composed of ``(root, closer)`` when provided a
    :term:`router` instance as the ``app`` argument.  The ``root``
    returned is the application root object.  The ``closer`` returned
    is a callable (accepting no arguments) that should be called when
    your scripting application is finished using the root.  If
    ``request`` is not None, it is used as the request passed to the
    :app:`Pyramid` application root factory.  A request is
    constructed and passed to the root factory if ``request`` is None."""
    your scripting application is finished using the root.
    ``request`` is passed to the :app:`Pyramid` application root
    factory to compute the root. If ``request`` is None, a default
    will be constructed using the registry's :term:`Request Factory`
    via the :meth:`pyramid.interfaces.IRequestFactory.blank` method.
    """
    registry = app.registry
    if request is None:
        request_factory = registry.queryUtility(
            IRequestFactory, default=Request)
        request = request_factory.blank('/')
        request.registry = registry
        request = _make_request('/', registry)
    threadlocals = {'registry':registry, 'request':request}
    app.threadlocal_manager.push(threadlocals)
    def closer(request=request): # keep request alive via this function default
@@ -23,3 +28,77 @@
    root = app.root_factory(request)
    return root, closer
def prepare(request=None, registry=None):
    """ This function pushes data onto the Pyramid threadlocal stack
    (request and registry), making those objects 'current'.  It
    returns a dictionary useful for bootstrapping a Pyramid
    application in a scripting environment.
    ``request`` is passed to the :app:`Pyramid` application root
    factory to compute the root. If ``request`` is None, a default
    will be constructed using the registry's :term:`Request Factory`
    via the :meth:`pyramid.interfaces.IRequestFactory.blank` method.
    If ``registry`` is not supplied, the last registry loaded from
    :attr:`pyramid.config.global_registries` will be used. If you
    have loaded more than one :app:`Pyramid` application in the
    current process, you may not want to use the last registry
    loaded, thus you can search the ``global_registries`` and supply
    the appropriate one based on your own criteria.
    The function returns a dictionary composed of ``root``,
    ``closer``, ``registry``, ``request`` and ``root_factory``.  The
    ``root`` returned is the application's root resource object.  The
    ``closer`` returned is a callable (accepting no arguments) that
    should be called when your scripting application is finished
    using the root.  ``registry`` is the registry object passed or
    the last registry loaded into
    :attr:`pyramid.config.global_registries` if no registry is passed.
    ``request`` is the request object passed or the constructed request
    if no request is passed.  ``root_factory`` is the root factory used
    to construct the root.
    """
    if registry is None:
        registry = getattr(request, 'registry', global_registries.last)
    if registry is None:
        raise ConfigurationError('No valid Pyramid applications could be '
                                 'found, make sure one has been created '
                                 'before trying to activate it.')
    if request is None:
        request = _make_request('/', registry)
    request.registry = registry
    threadlocals = {'registry':registry, 'request':request}
    threadlocal_manager.push(threadlocals)
    def closer():
        threadlocal_manager.pop()
    root_factory = registry.queryUtility(IRootFactory,
                                         default=DefaultRootFactory)
    root = root_factory(request)
    return {'root':root, 'closer':closer, 'registry':registry,
            'request':request, 'root_factory':root_factory}
def _make_request(path, registry=None):
    """ Return a :meth:`pyramid.request.Request` object anchored at a
    given path. The object returned will be generated from the supplied
    registry's :term:`Request Factory` using the
    :meth:`pyramid.interfaces.IRequestFactory.blank` method.
    This request object can be passed to :meth:`pyramid.scripting.get_root`
    or :meth:`pyramid.scripting.prepare` to initialize an application in
    preparation for executing a script with a proper environment setup.
    URLs can then be generated with the object, as well as rendering
    templates.
    If ``registry`` is not supplied, the last registry loaded from
    :attr:`pyramid.config.global_registries` will be used. If you have
    loaded more than one :app:`Pyramid` application in the current
    process, you may not want to use the last registry loaded, thus
    you can search the ``global_registries`` and supply the appropriate
    one based on your own criteria.
    """
    if registry is None:
        registry = global_registries.last
    request_factory = registry.queryUtility(IRequestFactory, default=Request)
    request = request_factory.blank(path)
    request.registry = registry
    return request
pyramid/security.py
@@ -24,6 +24,8 @@
ALL_PERMISSIONS = AllPermissionsList()
DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS)
NO_PERMISSION_REQUIRED = '__no_permission_required__'
def has_permission(permission, context, request):
    """ Provided a permission (a string or unicode object), a context
    (a :term:`resource` instance) and a request object, return an
pyramid/settings.py
@@ -20,35 +20,57 @@
        dict.__init__(self, d, **kw)
        eget = _environ_.get
        config_debug_all = self.get('debug_all', '')
        config_debug_all = self.get('pyramid.debug_all', config_debug_all)
        eff_debug_all = asbool(eget('PYRAMID_DEBUG_ALL', config_debug_all))
        config_reload_all = self.get('reload_all', '')
        eff_reload_all = asbool(eget('PYRAMID_RELOAD_ALL',config_reload_all))
        config_reload_all = self.get('pyramid.reload_all', config_reload_all)
        eff_reload_all = asbool(eget('PYRAMID_RELOAD_ALL', config_reload_all))
        config_debug_auth = self.get('debug_authorization', '')
        config_debug_auth = self.get('pyramid.debug_authorization',
                                     config_debug_auth)
        eff_debug_auth = asbool(eget('PYRAMID_DEBUG_AUTHORIZATION',
                                     config_debug_auth))
        config_debug_notfound = self.get('debug_notfound', '')
        config_debug_notfound = self.get('pyramid.debug_notfound',
                                         config_debug_notfound)
        eff_debug_notfound = asbool(eget('PYRAMID_DEBUG_NOTFOUND',
                                         config_debug_notfound))
        config_debug_routematch = self.get('debug_routematch', '')
        config_debug_routematch = self.get('pyramid.debug_routematch',
                                           config_debug_routematch)
        eff_debug_routematch = asbool(eget('PYRAMID_DEBUG_ROUTEMATCH',
                                         config_debug_routematch))
        config_debug_templates = self.get('debug_templates', '')
        config_debug_templates = self.get('pyramid.debug_templates',
                                          config_debug_templates)
        eff_debug_templates = asbool(eget('PYRAMID_DEBUG_TEMPLATES',
                                          config_debug_templates))
        config_reload_templates = self.get('reload_templates', '')
        config_reload_templates = self.get('pyramid.reload_templates',
                                           config_reload_templates)
        eff_reload_templates = asbool(eget('PYRAMID_RELOAD_TEMPLATES',
                                           config_reload_templates))
        config_reload_assets = self.get('reload_assets', '')
        config_reload_resources = self.get('reload_resources', '')
        config_reload_assets = self.get('pyramid.reload_assets',
                                        config_reload_assets)
        reload_assets = asbool(eget('PYRAMID_RELOAD_ASSETS',
                                    config_reload_assets))
        config_reload_resources = self.get('reload_resources', '')
        config_reload_resources = self.get('pyramid.reload_resources',
                                           config_reload_resources)
        reload_resources = asbool(eget('PYRAMID_RELOAD_RESOURCES',
                                    config_reload_resources))
        # reload_resources is an older alias for reload_assets
        eff_reload_assets = reload_assets or reload_resources
        locale_name = self.get('default_locale_name', 'en')
        locale_name = self.get('pyramid.default_locale_name', locale_name)
        eff_locale_name = eget('PYRAMID_DEFAULT_LOCALE_NAME', locale_name)
        config_prevent_http_cache = self.get('prevent_http_cache', '')
        config_prevent_http_cache = self.get('pyramid.prevent_http_cache',
                                             config_prevent_http_cache)
        eff_prevent_http_cache = asbool(eget('PYRAMID_PREVENT_HTTP_CACHE',
                                             config_prevent_http_cache))
        update = {
            'debug_authorization': eff_debug_all or eff_debug_auth,
            'debug_notfound': eff_debug_all or eff_debug_notfound,
@@ -58,6 +80,17 @@
            'reload_resources':eff_reload_all or eff_reload_assets,
            'reload_assets':eff_reload_all or eff_reload_assets,
            'default_locale_name':eff_locale_name,
            'prevent_http_cache':eff_prevent_http_cache,
            'pyramid.debug_authorization': eff_debug_all or eff_debug_auth,
            'pyramid.debug_notfound': eff_debug_all or eff_debug_notfound,
            'pyramid.debug_routematch': eff_debug_all or eff_debug_routematch,
            'pyramid.debug_templates': eff_debug_all or eff_debug_templates,
            'pyramid.reload_templates': eff_reload_all or eff_reload_templates,
            'pyramid.reload_resources':eff_reload_all or eff_reload_assets,
            'pyramid.reload_assets':eff_reload_all or eff_reload_assets,
            'pyramid.default_locale_name':eff_locale_name,
            'pyramid.prevent_http_cache':eff_prevent_http_cache,
            }
        self.update(update)
pyramid/static.py
@@ -14,6 +14,7 @@
from pyramid.interfaces import IStaticURLInfo
from pyramid.path import caller_package
from pyramid.request import call_app_with_subpath_as_path_info
from pyramid.security import NO_PERMISSION_REQUIRED
from pyramid.url import route_url
class PackageURLParser(StaticURLParser):
@@ -136,7 +137,8 @@
            # it's a view name
            cache_max_age = extra.pop('cache_max_age', None)
            # create a view
            view = static_view(spec, cache_max_age=cache_max_age)
            view = static_view(spec, cache_max_age=cache_max_age,
                               use_subpath=True)
            # Mutate extra to allow factory, etc to be passed through here.
            # Treat permission specially because we'd like to default to
@@ -148,7 +150,7 @@
            if permission is None:
                permission = extra.pop('permission', None)
            if permission is None:
                permission = '__no_permission_required__'
                permission = NO_PERMISSION_REQUIRED
            context = extra.pop('view_context', None)
            if context is None:
@@ -199,6 +201,13 @@
    response headers returned by the view (default is 3600 seconds or
    five minutes).
    ``use_subpath`` influences whether ``request.subpath`` will be used as
    ``PATH_INFO`` when calling the underlying WSGI application which actually
    serves the static files.  If it is ``True``, the static application will
    consider ``request.subpath`` as ``PATH_INFO`` input.  If it is ``False``,
    the static application will consider request.path_info as ``PATH_INFO``
    input. By default, this is ``False``.
    .. note:: If the ``root_dir`` is relative to a :term:`package`, or
         is a :term:`asset specification` the :app:`Pyramid`
         :class:`pyramid.config.Configurator` method can be
@@ -207,7 +216,8 @@
         absolute, configuration will not be able to
         override the assets it contains.  """
    
    def __init__(self, root_dir, cache_max_age=3600, package_name=None):
    def __init__(self, root_dir, cache_max_age=3600, package_name=None,
                 use_subpath=False):
        # package_name is for bw compat; it is preferred to pass in a
        # package-relative path as root_dir
        # (e.g. ``anotherpackage:foo/static``).
@@ -220,6 +230,9 @@
            app = PackageURLParser(
                package_name, root_dir, cache_max_age=cache_max_age)
        self.app = app
        self.use_subpath = use_subpath
    def __call__(self, context, request):
        return call_app_with_subpath_as_path_info(request, self.app)
        if self.use_subpath:
            return call_app_with_subpath_as_path_info(request, self.app)
        return request.get_response(self.app)
pyramid/tests/defpermbugapp/__init__.py
@@ -1,4 +1,5 @@
from webob import Response
from pyramid.security import NO_PERMISSION_REQUIRED
from pyramid.view import view_config
@view_config(name='x')
@@ -9,7 +10,7 @@
def y_view(request): # pragma: no cover
     return Response('this is private too!')
     
@view_config(name='z', permission='__no_permission_required__')
@view_config(name='z', permission=NO_PERMISSION_REQUIRED)
def z_view(request):
     return Response('this is public')
pyramid/tests/grokkedapp/__init__.py
@@ -1,15 +1,16 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config()
@view_config(renderer=null_renderer)
def grokked(context, request):
    return 'grokked'
@view_config(request_method='POST')
@view_config(request_method='POST', renderer=null_renderer)
def grokked_post(context, request):
    return 'grokked_post'
@view_config(name='stacked2')
@view_config(name='stacked1')
@view_config(name='stacked2', renderer=null_renderer)
@view_config(name='stacked1', renderer=null_renderer)
def stacked(context, request):
    return 'stacked'
@@ -21,8 +22,10 @@
    def __call__(self):
        return 'stacked_class'
stacked_class = view_config(name='stacked_class1')(stacked_class)
stacked_class = view_config(name='stacked_class2')(stacked_class)
stacked_class = view_config(name='stacked_class1',
                            renderer=null_renderer)(stacked_class)
stacked_class = view_config(name='stacked_class2',
                            renderer=null_renderer)(stacked_class)
    
class oldstyle_grokked_class:
    def __init__(self, context, request):
@@ -32,7 +35,8 @@
    def __call__(self):
        return 'oldstyle_grokked_class'
    
oldstyle_grokked_class = view_config(name='oldstyle_grokked_class')(
oldstyle_grokked_class = view_config(name='oldstyle_grokked_class',
                                     renderer=null_renderer)(
    oldstyle_grokked_class)
class grokked_class(object):
@@ -43,17 +47,19 @@
    def __call__(self):
        return 'grokked_class'
        
grokked_class = view_config(name='grokked_class')(grokked_class)
grokked_class = view_config(name='grokked_class',
                            renderer=null_renderer)(grokked_class)
class Foo(object):
    def __call__(self, context, request):
        return 'grokked_instance'
grokked_instance = Foo()
grokked_instance = view_config(name='grokked_instance')(grokked_instance)
grokked_instance = view_config(name='grokked_instance',
                               renderer=null_renderer)(grokked_instance)
class Base(object):
    @view_config(name='basemethod')
    @view_config(name='basemethod', renderer=null_renderer)
    def basemethod(self):
        """ """
    
@@ -62,16 +68,16 @@
        self.context = context
        self.request = request
    @view_config(name='method1')
    @view_config(name='method1', renderer=null_renderer)
    def method1(self):
        return 'method1'
    @view_config(name='method2')
    @view_config(name='method2', renderer=null_renderer)
    def method2(self):
        return 'method2'
    @view_config(name='stacked_method2')
    @view_config(name='stacked_method1')
    @view_config(name='stacked_method2', renderer=null_renderer)
    @view_config(name='stacked_method1', renderer=null_renderer)
    def stacked(self):
        return 'stacked_method'
pyramid/tests/grokkedapp/another.py
@@ -1,15 +1,16 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(name='another')
@view_config(name='another', renderer=null_renderer)
def grokked(context, request):
    return 'another_grokked'
@view_config(request_method='POST', name='another')
@view_config(request_method='POST', name='another', renderer=null_renderer)
def grokked_post(context, request):
    return 'another_grokked_post'
@view_config(name='another_stacked2')
@view_config(name='another_stacked1')
@view_config(name='another_stacked2', renderer=null_renderer)
@view_config(name='another_stacked1', renderer=null_renderer)
def stacked(context, request):
    return 'another_stacked'
@@ -21,8 +22,10 @@
    def __call__(self):
        return 'another_stacked_class'
stacked_class = view_config(name='another_stacked_class1')(stacked_class)
stacked_class = view_config(name='another_stacked_class2')(stacked_class)
stacked_class = view_config(name='another_stacked_class1',
                            renderer=null_renderer)(stacked_class)
stacked_class = view_config(name='another_stacked_class2',
                            renderer=null_renderer)(stacked_class)
class oldstyle_grokked_class:
    def __init__(self, context, request):
@@ -32,7 +35,8 @@
    def __call__(self):
        return 'another_oldstyle_grokked_class'
    
oldstyle_grokked_class = view_config(name='another_oldstyle_grokked_class')(
oldstyle_grokked_class = view_config(name='another_oldstyle_grokked_class',
                                     renderer=null_renderer)(
    oldstyle_grokked_class)
class grokked_class(object):
@@ -43,14 +47,16 @@
    def __call__(self):
        return 'another_grokked_class'
        
grokked_class = view_config(name='another_grokked_class')(grokked_class)
grokked_class = view_config(name='another_grokked_class',
                            renderer=null_renderer)(grokked_class)
class Foo(object):
    def __call__(self, context, request):
        return 'another_grokked_instance'
grokked_instance = Foo()
grokked_instance = view_config(name='another_grokked_instance')(
grokked_instance = view_config(name='another_grokked_instance',
                               renderer=null_renderer)(
    grokked_instance)
# ungrokkable
pyramid/tests/grokkedapp/pod/notinit.py
@@ -1,5 +1,6 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(name='pod_notinit')
@view_config(name='pod_notinit', renderer=null_renderer)
def subpackage_notinit(context, request):
    return 'pod_notinit'
pyramid/tests/grokkedapp/subpackage/__init__.py
@@ -1,5 +1,6 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(name='subpackage_init')
@view_config(name='subpackage_init', renderer=null_renderer)
def subpackage_init(context, request):
    return 'subpackage_init'
pyramid/tests/grokkedapp/subpackage/notinit.py
@@ -1,5 +1,6 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(name='subpackage_notinit')
@view_config(name='subpackage_notinit', renderer=null_renderer)
def subpackage_notinit(context, request):
    return 'subpackage_notinit'
pyramid/tests/grokkedapp/subpackage/subsubpackage/__init__.py
@@ -1,5 +1,6 @@
from pyramid.view import view_config
from pyramid.renderers import null_renderer
@view_config(name='subsubpackage_init')
@view_config(name='subsubpackage_init', renderer=null_renderer)
def subpackage_init(context, request):
    return 'subsubpackage_init'
pyramid/tests/restbugapp/views.py
@@ -1,4 +1,4 @@
from webob import Response
from pyramid.response import Response
class BaseRESTView(object):
    def __init__(self, context, request):
pyramid/tests/test_compat.py
File was deleted
pyramid/tests/test_config.py
@@ -10,7 +10,8 @@
class ConfiguratorTests(unittest.TestCase):
    def _makeOne(self, *arg, **kw):
        from pyramid.config import Configurator
        return Configurator(*arg, **kw)
        config = Configurator(*arg, **kw)
        return config
    def _registerRenderer(self, config, name='.txt'):
        from pyramid.interfaces import IRendererFactory
@@ -152,7 +153,7 @@
        from pyramid.interfaces import IDebugLogger
        config = self._makeOne()
        logger = config.registry.getUtility(IDebugLogger)
        self.assertEqual(logger.name, 'pyramid.debug')
        self.assertEqual(logger.name, 'pyramid.tests')
    def test_ctor_noreg_debug_logger_non_None(self):
        from pyramid.interfaces import IDebugLogger
@@ -211,7 +212,7 @@
        view = self._getViewCallable(config,
                                     ctx_iface=IExceptionResponse,
                                     request_iface=IRequest)
        self.assertTrue(view is default_exceptionresponse_view)
        self.assertTrue(view.__wraps__ is default_exceptionresponse_view)
    def test_ctor_exceptionresponse_view_None(self):
        from pyramid.interfaces import IExceptionResponse
@@ -230,7 +231,7 @@
        view = self._getViewCallable(config,
                                     ctx_iface=IExceptionResponse,
                                     request_iface=IRequest)
        self.assertTrue(view is exceptionresponse_view)
        self.assertTrue(view.__wraps__ is exceptionresponse_view)
    def test_with_package_module(self):
        from pyramid.tests import test_configuration
@@ -336,6 +337,7 @@
        reg = DummyRegistry()
        config = self._makeOne(reg)
        config.add_view = lambda *arg, **kw: False
        config._add_tween = lambda *arg, **kw: False
        config.setup_registry()
        self.assertEqual(reg.has_listeners, True)
@@ -347,6 +349,7 @@
        config = self._makeOne(reg)
        views = []
        config.add_view = lambda *arg, **kw: views.append((arg, kw))
        config._add_tween = lambda *arg, **kw: False
        config.setup_registry()
        self.assertEqual(views[0], ((default_exceptionresponse_view,),
                                    {'context':IExceptionResponse}))
@@ -363,6 +366,7 @@
            config.registry.queryAdapter(response, IResponse) is response)
    def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.httpexceptions import HTTPNotFound
@@ -372,7 +376,7 @@
        config.setup_registry() # registers IExceptionResponse default view
        def myview(context, request):
            return 'OK'
        config.add_view(myview, context=HTTPNotFound)
        config.add_view(myview, context=HTTPNotFound, renderer=null_renderer)
        request = self._makeRequest(config)
        view = self._getViewCallable(config,
                                     ctx_iface=implementedBy(HTTPNotFound),
@@ -400,7 +404,7 @@
        config = self._makeOne(reg)
        config.setup_registry()
        logger = reg.getUtility(IDebugLogger)
        self.assertEqual(logger.name, 'pyramid.debug')
        self.assertEqual(logger.name, 'pyramid.tests')
    def test_setup_registry_debug_logger_non_None(self):
        from pyramid.registry import Registry
@@ -412,15 +416,14 @@
        result = reg.getUtility(IDebugLogger)
        self.assertEqual(logger, result)
    def test_setup_registry_debug_logger_dottedname(self):
    def test_setup_registry_debug_logger_name(self):
        from pyramid.registry import Registry
        from pyramid.interfaces import IDebugLogger
        reg = Registry()
        config = self._makeOne(reg)
        config.setup_registry(debug_logger='pyramid.tests')
        config.setup_registry(debug_logger='foo')
        result = reg.getUtility(IDebugLogger)
        import pyramid.tests
        self.assertEqual(result, pyramid.tests)
        self.assertEqual(result.name, 'foo')
    def test_setup_registry_authentication_policy(self):
        from pyramid.registry import Registry
@@ -569,6 +572,33 @@
        config.setup_registry(default_permission='view')
        self.assertEqual(reg.getUtility(IDefaultPermission), 'view')
    def test_setup_registry_includes(self):
        from pyramid.registry import Registry
        reg = Registry()
        config = self._makeOne(reg)
        settings = {
            'pyramid.include': """pyramid.tests.test_config.dummy_include
pyramid.tests.test_config.dummy_include2""",
        }
        config.setup_registry(settings=settings)
        self.assert_(reg.included)
        self.assert_(reg.also_included)
    def test_setup_registry_tweens(self):
        from pyramid.interfaces import ITweens
        from pyramid.registry import Registry
        reg = Registry()
        config = self._makeOne(reg)
        settings = {
            'pyramid.tweens': 'pyramid.tests.test_config.dummy_tween_factory'
        }
        config.setup_registry(settings=settings)
        config.commit()
        tweens = config.registry.getUtility(ITweens)
        self.assertEqual(tweens.explicit,
                         [('pyramid.tests.test_config.dummy_tween_factory',
                           dummy_tween_factory)])
    def test_get_settings_nosettings(self):
        from pyramid.registry import Registry
        reg = Registry()
@@ -599,6 +629,136 @@
        config.add_settings({'a':1})
        settings = reg.getUtility(ISettings)
        self.assertEqual(settings['a'], 1)
    def test_add_tweens_names_distinct(self):
        from pyramid.interfaces import ITweens
        from pyramid.tweens import excview_tween_factory
        def factory1(handler, registry): return handler
        def factory2(handler, registry): return handler
        config = self._makeOne()
        config.add_tween(factory1)
        config.add_tween(factory2)
        config.commit()
        tweens = config.registry.queryUtility(ITweens)
        implicit = tweens.implicit()
        self.assertEqual(
            implicit,
            [
                ('pyramid.tests.test_config.factory2', factory2),
                ('pyramid.tests.test_config.factory1', factory1),
                ('pyramid.tweens.excview_tween_factory', excview_tween_factory),
                ]
            )
    def test_add_tweens_names_with_underover(self):
        from pyramid.interfaces import ITweens
        from pyramid.tweens import excview_tween_factory
        from pyramid.tweens import MAIN
        def factory1(handler, registry): return handler
        def factory2(handler, registry): return handler
        config = self._makeOne()
        config.add_tween(factory1, over=MAIN)
        config.add_tween(factory2, over=MAIN,
                         under='pyramid.tests.test_config.factory1')
        config.commit()
        tweens = config.registry.queryUtility(ITweens)
        implicit = tweens.implicit()
        self.assertEqual(
            implicit,
            [
                ('pyramid.tweens.excview_tween_factory', excview_tween_factory),
                ('pyramid.tests.test_config.factory1', factory1),
                ('pyramid.tests.test_config.factory2', factory2),
             ])
    def test_add_tween_dottedname(self):
        from pyramid.interfaces import ITweens
        from pyramid.tweens import excview_tween_factory
        config = self._makeOne()
        config.add_tween('pyramid.tests.test_config.dummy_tween_factory')
        config.commit()
        tweens = config.registry.queryUtility(ITweens)
        self.assertEqual(
            tweens.implicit(),
            [
                ('pyramid.tests.test_config.dummy_tween_factory',
                 dummy_tween_factory),
                ('pyramid.tweens.excview_tween_factory', excview_tween_factory),
                ])
    def test_add_tween_instance(self):
        from pyramid.interfaces import ITweens
        from pyramid.tweens import excview_tween_factory
        class ATween(object): pass
        atween = ATween()
        config = self._makeOne()
        config.add_tween(atween)
        config.commit()
        tweens = config.registry.queryUtility(ITweens)
        implicit = tweens.implicit()
        self.assertEqual(len(implicit), 2)
        self.assertTrue(
          implicit[0][0].startswith(
                'pyramid.tests.test_config.ATween.'))
        self.assertEqual(implicit[0][1], atween)
        self.assertEqual(
            implicit[1],
            ('pyramid.tweens.excview_tween_factory', excview_tween_factory))
    def test_add_tween_unsuitable(self):
        from pyramid.exceptions import ConfigurationError
        import pyramid.tests
        config = self._makeOne()
        self.assertRaises(ConfigurationError, config.add_tween, pyramid.tests)
    def test_add_tween_alias_ingress(self):
        from pyramid.exceptions import ConfigurationError
        from pyramid.tweens import INGRESS
        config = self._makeOne()
        self.assertRaises(ConfigurationError,
            config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory',
            alias=INGRESS)
    def test_add_tween_alias_main(self):
        from pyramid.exceptions import ConfigurationError
        from pyramid.tweens import MAIN
        config = self._makeOne()
        self.assertRaises(ConfigurationError,
            config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory',
            alias=MAIN)
    def test_add_tweens_conflict(self):
        from zope.configuration.config import ConfigurationConflictError
        config = self._makeOne()
        config.add_tween('pyramid.tests.test_config.dummy_tween_factory')
        config.add_tween('pyramid.tests.test_config.dummy_tween_factory')
        self.assertRaises(ConfigurationConflictError, config.commit)
    def test_add_tweens_conflict_same_alias(self):
        from zope.configuration.config import ConfigurationConflictError
        class ATween(object): pass
        atween1 = ATween()
        atween2 = ATween()
        config = self._makeOne()
        config.add_tween(atween1, alias='a')
        config.add_tween(atween2, alias='a')
        self.assertRaises(ConfigurationConflictError, config.commit)
    def test_add_tween_over_ingress(self):
        from pyramid.exceptions import ConfigurationError
        from pyramid.tweens import INGRESS
        config = self._makeOne()
        self.assertRaises(ConfigurationError,
            config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory',
            over=INGRESS)
    def test_add_tween_under_main(self):
        from pyramid.exceptions import ConfigurationError
        from pyramid.tweens import MAIN
        config = self._makeOne()
        self.assertRaises(ConfigurationError,
            config.add_tween, 'pyramid.tests.test_config.dummy_tween_factory',
            under=MAIN)
    def test_add_subscriber_defaults(self):
        from zope.interface import implements
@@ -671,6 +831,7 @@
        self.assertEqual(len(L), 1)
    def test_make_wsgi_app(self):
        import pyramid.config
        from pyramid.router import Router
        from pyramid.interfaces import IApplicationCreated
        manager = DummyThreadLocalManager()
@@ -682,8 +843,29 @@
        self.assertEqual(manager.pushed['registry'], config.registry)
        self.assertEqual(manager.pushed['request'], None)
        self.assertTrue(manager.popped)
        self.assertEqual(pyramid.config.global_registries.last, app.registry)
        self.assertEqual(len(subscriber), 1)
        self.assertTrue(IApplicationCreated.providedBy(subscriber[0]))
        pyramid.config.global_registries.empty()
    def test_global_registries_empty(self):
        from pyramid.config import global_registries
        self.assertEqual(global_registries.last, None)
    def test_global_registries(self):
        from pyramid.config import global_registries
        global_registries.empty()
        config1 = self._makeOne()
        config1.make_wsgi_app()
        self.assertEqual(global_registries.last, config1.registry)
        config2 = self._makeOne()
        config2.make_wsgi_app()
        self.assertEqual(global_registries.last, config2.registry)
        self.assertEqual(list(global_registries),
                         [config1.registry, config2.registry])
        global_registries.remove(config2.registry)
        self.assertEqual(global_registries.last, config1.registry)
        global_registries.empty()
    def test_include_with_dotted_name(self):
        from pyramid import tests
@@ -767,12 +949,14 @@
                          None, True, True)
    def test_add_view_with_request_type(self):
        from pyramid.renderers import null_renderer
        from zope.interface import directlyProvides
        from pyramid.interfaces import IRequest
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view,
                        request_type='pyramid.interfaces.IRequest')
                        request_type='pyramid.interfaces.IRequest',
                        renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = DummyRequest()
        self._assertNotFound(wrapper, None, request)
@@ -798,29 +982,34 @@
        self.assertEqual(wrapper.__doc__, view.__doc__)
    def test_add_view_view_callable_dottedname(self):
        from pyramid.renderers import null_renderer
        config = self._makeOne(autocommit=True)
        config.add_view(view='pyramid.tests.test_config.dummy_view')
        config.add_view(view='pyramid.tests.test_config.dummy_view',
                        renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        self.assertEqual(wrapper(None, None), 'OK')
    def test_add_view_with_function_callable(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view)
        config.add_view(view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        result = wrapper(None, None)
        self.assertEqual(result, 'OK')
    def test_add_view_with_function_callable_requestonly(self):
        from pyramid.renderers import null_renderer
        def view(request):
            return 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view)
        config.add_view(view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        result = wrapper(None, None)
        self.assertEqual(result, 'OK')
    def test_add_view_with_decorator(self):
        from pyramid.renderers import null_renderer
        def view(request):
            """ ABC """
            return 'OK'
@@ -829,7 +1018,8 @@
                return fn(context, request)
            return inner
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, decorator=view_wrapper)
        config.add_view(view=view, decorator=view_wrapper,
                        renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        self.assertFalse(wrapper is view)
        self.assertEqual(wrapper.__doc__, view.__doc__)
@@ -858,30 +1048,33 @@
        assert_similar_datetime(expires, when)
    def test_add_view_as_instance(self):
        from pyramid.renderers import null_renderer
        class AView:
            def __call__(self, context, request):
                """ """
                return 'OK'
        view = AView()
        config = self._makeOne(autocommit=True)
        config.add_view(view=view)
        config.add_view(view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        result = wrapper(None, None)
        self.assertEqual(result, 'OK')
    def test_add_view_as_instance_requestonly(self):
        from pyramid.renderers import null_renderer
        class AView:
            def __call__(self, request):
                """ """
                return 'OK'
        view = AView()
        config = self._makeOne(autocommit=True)
        config.add_view(view=view)
        config.add_view(view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        result = wrapper(None, None)
        self.assertEqual(result, 'OK')
    def test_add_view_as_oldstyle_class(self):
        from pyramid.renderers import null_renderer
        class view:
            def __init__(self, context, request):
                self.context = context
@@ -890,7 +1083,7 @@
            def __call__(self):
                return 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view)
        config.add_view(view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        result = wrapper(None, request)
@@ -898,6 +1091,7 @@
        self.assertEqual(request.__view__.__class__, view)
    def test_add_view_as_oldstyle_class_requestonly(self):
        from pyramid.renderers import null_renderer
        class view:
            def __init__(self, request):
                self.request = request
@@ -905,7 +1099,7 @@
            def __call__(self):
                return 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view)
        config.add_view(view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
@@ -914,70 +1108,79 @@
        self.assertEqual(request.__view__.__class__, view)
    def test_add_view_context_as_class(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        view = lambda *arg: 'OK'
        class Foo:
            pass
        config = self._makeOne(autocommit=True)
        config.add_view(context=Foo, view=view)
        config.add_view(context=Foo, view=view, renderer=null_renderer)
        foo = implementedBy(Foo)
        wrapper = self._getViewCallable(config, foo)
        self.assertEqual(wrapper, view)
    def test_add_view_context_as_iface(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(context=IDummy, view=view)
        config.add_view(context=IDummy, view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config, IDummy)
        self.assertEqual(wrapper, view)
    def test_add_view_context_as_dottedname(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(context='pyramid.tests.test_config.IDummy',
                        view=view)
                        view=view,  renderer=null_renderer)
        wrapper = self._getViewCallable(config, IDummy)
        self.assertEqual(wrapper, view)
    def test_add_view_for__as_dottedname(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(for_='pyramid.tests.test_config.IDummy',
                        view=view)
                        view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config, IDummy)
        self.assertEqual(wrapper, view)
    def test_add_view_for_as_class(self):
        # ``for_`` is older spelling for ``context``
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        view = lambda *arg: 'OK'
        class Foo:
            pass
        config = self._makeOne(autocommit=True)
        config.add_view(for_=Foo, view=view)
        config.add_view(for_=Foo, view=view, renderer=null_renderer)
        foo = implementedBy(Foo)
        wrapper = self._getViewCallable(config, foo)
        self.assertEqual(wrapper, view)
    def test_add_view_for_as_iface(self):
        # ``for_`` is older spelling for ``context``
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(for_=IDummy, view=view)
        config.add_view(for_=IDummy, view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config, IDummy)
        self.assertEqual(wrapper, view)
    def test_add_view_context_trumps_for(self):
        # ``for_`` is older spelling for ``context``
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        class Foo:
            pass
        config.add_view(context=IDummy, for_=Foo, view=view)
        config.add_view(context=IDummy, for_=Foo, view=view,
                        renderer=null_renderer)
        wrapper = self._getViewCallable(config, IDummy)
        self.assertEqual(wrapper, view)
    def test_add_view_register_secured_view(self):
        from pyramid.renderers import null_renderer
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import ISecuredView
@@ -985,13 +1188,14 @@
        view = lambda *arg: 'OK'
        view.__call_permissive__ = view
        config = self._makeOne(autocommit=True)
        config.add_view(view=view)
        config.add_view(view=view, renderer=null_renderer)
        wrapper = config.registry.adapters.lookup(
            (IViewClassifier, IRequest, Interface),
            ISecuredView, name='', default=None)
        self.assertEqual(wrapper, view)
    def test_add_view_exception_register_secured_view(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -999,14 +1203,15 @@
        view = lambda *arg: 'OK'
        view.__call_permissive__ = view
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, context=RuntimeError)
        config.add_view(view=view, context=RuntimeError, renderer=null_renderer)
        wrapper = config.registry.adapters.lookup(
            (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)),
            IView, name='', default=None)
        self.assertEqual(wrapper, view)
    def test_add_view_same_phash_overrides_existing_single_view(self):
        from pyramid.compat import md5
        from pyramid.renderers import null_renderer
        from hashlib import md5
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1021,7 +1226,7 @@
            view, (IViewClassifier, IRequest, Interface), IView, name='')
        def newview(context, request):
            return 'OK'
        config.add_view(view=newview, xhr=True)
        config.add_view(view=newview, xhr=True, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        self.assertFalse(IMultiView.providedBy(wrapper))
        request = DummyRequest()
@@ -1029,7 +1234,8 @@
        self.assertEqual(wrapper(None, request), 'OK')
    def test_add_view_exc_same_phash_overrides_existing_single_view(self):
        from pyramid.compat import md5
        from pyramid.renderers import null_renderer
        from hashlib import md5
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1046,7 +1252,8 @@
            IView, name='')
        def newview(context, request):
            return 'OK'
        config.add_view(view=newview, xhr=True, context=RuntimeError)
        config.add_view(view=newview, xhr=True, context=RuntimeError,
                        renderer=null_renderer)
        wrapper = self._getViewCallable(
            config, ctx_iface=implementedBy(RuntimeError), exception_view=True)
        self.assertFalse(IMultiView.providedBy(wrapper))
@@ -1055,6 +1262,7 @@
        self.assertEqual(wrapper(None, request), 'OK')
    def test_add_view_default_phash_overrides_no_phash(self):
        from pyramid.renderers import null_renderer
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1066,7 +1274,7 @@
            view, (IViewClassifier, IRequest, Interface), IView, name='')
        def newview(context, request):
            return 'OK'
        config.add_view(view=newview)
        config.add_view(view=newview, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        self.assertFalse(IMultiView.providedBy(wrapper))
        request = DummyRequest()
@@ -1074,6 +1282,7 @@
        self.assertEqual(wrapper(None, request), 'OK')
    def test_add_view_exc_default_phash_overrides_no_phash(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1087,7 +1296,8 @@
            IView, name='')
        def newview(context, request):
            return 'OK'
        config.add_view(view=newview, context=RuntimeError)
        config.add_view(view=newview, context=RuntimeError,
                        renderer=null_renderer)
        wrapper = self._getViewCallable(
            config, ctx_iface=implementedBy(RuntimeError), exception_view=True)
        self.assertFalse(IMultiView.providedBy(wrapper))
@@ -1096,6 +1306,7 @@
        self.assertEqual(wrapper(None, request), 'OK')
    def test_add_view_default_phash_overrides_default_phash(self):
        from pyramid.renderers import null_renderer
        from pyramid.config import DEFAULT_PHASH
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
@@ -1109,7 +1320,7 @@
            view, (IViewClassifier, IRequest, Interface), IView, name='')
        def newview(context, request):
            return 'OK'
        config.add_view(view=newview)
        config.add_view(view=newview, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        self.assertFalse(IMultiView.providedBy(wrapper))
        request = DummyRequest()
@@ -1117,6 +1328,7 @@
        self.assertEqual(wrapper(None, request), 'OK')
    def test_add_view_exc_default_phash_overrides_default_phash(self):
        from pyramid.renderers import null_renderer
        from pyramid.config import DEFAULT_PHASH
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
@@ -1132,7 +1344,8 @@
            IView, name='')
        def newview(context, request):
            return 'OK'
        config.add_view(view=newview, context=RuntimeError)
        config.add_view(view=newview, context=RuntimeError,
                        renderer=null_renderer)
        wrapper = self._getViewCallable(
            config, ctx_iface=implementedBy(RuntimeError), exception_view=True)
        self.assertFalse(IMultiView.providedBy(wrapper))
@@ -1141,6 +1354,7 @@
        self.assertEqual(wrapper(None, request), 'OK')
    def test_add_view_multiview_replaces_existing_view(self):
        from pyramid.renderers import null_renderer
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1151,12 +1365,13 @@
        config = self._makeOne(autocommit=True)
        config.registry.registerAdapter(
            view, (IViewClassifier, IRequest, Interface), IView, name='')
        config.add_view(view=view)
        config.add_view(view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        self.assertTrue(IMultiView.providedBy(wrapper))
        self.assertEqual(wrapper(None, None), 'OK')
    def test_add_view_exc_multiview_replaces_existing_view(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1174,13 +1389,15 @@
            view,
            (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)),
            IView, name='')
        config.add_view(view=view, context=RuntimeError)
        config.add_view(view=view, context=RuntimeError,
                        renderer=null_renderer)
        wrapper = self._getViewCallable(
            config, ctx_iface=implementedBy(RuntimeError), exception_view=True)
        self.assertTrue(IMultiView.providedBy(wrapper))
        self.assertEqual(wrapper(None, None), 'OK')
    def test_add_view_multiview_replaces_existing_securedview(self):
        from pyramid.renderers import null_renderer
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import ISecuredView
@@ -1192,12 +1409,13 @@
        config.registry.registerAdapter(
            view, (IViewClassifier, IRequest, Interface),
            ISecuredView, name='')
        config.add_view(view=view)
        config.add_view(view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        self.assertTrue(IMultiView.providedBy(wrapper))
        self.assertEqual(wrapper(None, None), 'OK')
    def test_add_view_exc_multiview_replaces_existing_securedview(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import ISecuredView
@@ -1215,13 +1433,14 @@
            view,
            (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)),
            ISecuredView, name='')
        config.add_view(view=view, context=RuntimeError)
        config.add_view(view=view, context=RuntimeError, renderer=null_renderer)
        wrapper = self._getViewCallable(
            config, ctx_iface=implementedBy(RuntimeError), exception_view=True)
        self.assertTrue(IMultiView.providedBy(wrapper))
        self.assertEqual(wrapper(None, None), 'OK')
    def test_add_view_with_accept_multiview_replaces_existing_view(self):
        from pyramid.renderers import null_renderer
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1234,7 +1453,7 @@
        config = self._makeOne(autocommit=True)
        config.registry.registerAdapter(
            view, (IViewClassifier, IRequest, Interface), IView, name='')
        config.add_view(view=view2, accept='text/html')
        config.add_view(view=view2, accept='text/html', renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        self.assertTrue(IMultiView.providedBy(wrapper))
        self.assertEqual(len(wrapper.views), 1)
@@ -1245,6 +1464,7 @@
        self.assertEqual(wrapper(None, request), 'OK2')
    def test_add_view_exc_with_accept_multiview_replaces_existing_view(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1264,7 +1484,8 @@
            view,
            (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)),
            IView, name='')
        config.add_view(view=view2, accept='text/html', context=RuntimeError)
        config.add_view(view=view2, accept='text/html', context=RuntimeError,
                        renderer=null_renderer)
        wrapper = self._getViewCallable(
            config, ctx_iface=implementedBy(RuntimeError), exception_view=True)
        self.assertTrue(IMultiView.providedBy(wrapper))
@@ -1276,6 +1497,7 @@
        self.assertEqual(wrapper(None, request), 'OK2')
    def test_add_view_multiview_replaces_existing_view_with___accept__(self):
        from pyramid.renderers import null_renderer
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1290,7 +1512,7 @@
        config = self._makeOne(autocommit=True)
        config.registry.registerAdapter(
            view, (IViewClassifier, IRequest, Interface), IView, name='')
        config.add_view(view=view2)
        config.add_view(view=view2, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        self.assertTrue(IMultiView.providedBy(wrapper))
        self.assertEqual(len(wrapper.views), 1)
@@ -1301,6 +1523,7 @@
        self.assertEqual(wrapper(None, request), 'OK')
    def test_add_view_exc_mulview_replaces_existing_view_with___accept__(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1322,7 +1545,8 @@
            view,
            (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)),
            IView, name='')
        config.add_view(view=view2, context=RuntimeError)
        config.add_view(view=view2, context=RuntimeError,
                        renderer=null_renderer)
        wrapper = self._getViewCallable(
            config, ctx_iface=implementedBy(RuntimeError), exception_view=True)
        self.assertTrue(IMultiView.providedBy(wrapper))
@@ -1334,6 +1558,7 @@
        self.assertEqual(wrapper(None, request), 'OK')
    def test_add_view_multiview_replaces_multiview(self):
        from pyramid.renderers import null_renderer
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IMultiView
@@ -1344,13 +1569,14 @@
            view, (IViewClassifier, IRequest, Interface),
            IMultiView, name='')
        view2 = lambda *arg: 'OK2'
        config.add_view(view=view2)
        config.add_view(view=view2, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        self.assertTrue(IMultiView.providedBy(wrapper))
        self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)])
        self.assertEqual(wrapper(None, None), 'OK1')
    def test_add_view_exc_multiview_replaces_multiview(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IMultiView
@@ -1367,7 +1593,8 @@
            (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)),
            IMultiView, name='')
        view2 = lambda *arg: 'OK2'
        config.add_view(view=view2, context=RuntimeError)
        config.add_view(view=view2, context=RuntimeError,
                        renderer=null_renderer)
        wrapper = self._getViewCallable(
            config, ctx_iface=implementedBy(RuntimeError), exception_view=True)
        self.assertTrue(IMultiView.providedBy(wrapper))
@@ -1375,6 +1602,7 @@
        self.assertEqual(wrapper(None, None), 'OK1')
    def test_add_view_multiview_context_superclass_then_subclass(self):
        from pyramid.renderers import null_renderer
        from zope.interface import Interface
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1389,7 +1617,7 @@
        config = self._makeOne(autocommit=True)
        config.registry.registerAdapter(
            view, (IViewClassifier, IRequest, ISuper), IView, name='')
        config.add_view(view=view2, for_=ISub)
        config.add_view(view=view2, for_=ISub, renderer=null_renderer)
        wrapper = self._getViewCallable(config, ISuper, IRequest)
        self.assertFalse(IMultiView.providedBy(wrapper))
        self.assertEqual(wrapper(None, None), 'OK')
@@ -1398,6 +1626,7 @@
        self.assertEqual(wrapper(None, None), 'OK2')
    def test_add_view_multiview_exception_superclass_then_subclass(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IView
@@ -1415,7 +1644,7 @@
            view, (IViewClassifier, IRequest, Super), IView, name='')
        config.registry.registerAdapter(
            view, (IExceptionViewClassifier, IRequest, Super), IView, name='')
        config.add_view(view=view2, for_=Sub)
        config.add_view(view=view2, for_=Sub, renderer=null_renderer)
        wrapper = self._getViewCallable(
            config, implementedBy(Super), IRequest)
        wrapper_exc_view = self._getViewCallable(
@@ -1432,6 +1661,7 @@
        self.assertEqual(wrapper_exc_view(None, None), 'OK2')
    def test_add_view_multiview_call_ordering(self):
        from pyramid.renderers import null_renderer as nr
        from zope.interface import directlyProvides
        def view1(context, request): return 'view1'
        def view2(context, request): return 'view2'
@@ -1442,15 +1672,18 @@
        def view7(context, request): return 'view7'
        def view8(context, request): return 'view8'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view1)
        config.add_view(view=view2, request_method='POST')
        config.add_view(view=view3,request_param='param')
        config.add_view(view=view4, containment=IDummy)
        config.add_view(view=view5, request_method='POST',request_param='param')
        config.add_view(view=view6, request_method='POST', containment=IDummy)
        config.add_view(view=view7, request_param='param', containment=IDummy)
        config.add_view(view=view1, renderer=nr)
        config.add_view(view=view2, request_method='POST', renderer=nr)
        config.add_view(view=view3,request_param='param', renderer=nr)
        config.add_view(view=view4, containment=IDummy, renderer=nr)
        config.add_view(view=view5, request_method='POST',
                        request_param='param',  renderer=nr)
        config.add_view(view=view6, request_method='POST', containment=IDummy,
                        renderer=nr)
        config.add_view(view=view7, request_param='param', containment=IDummy,
                        renderer=nr)
        config.add_view(view=view8, request_method='POST',request_param='param',
                        containment=IDummy)
                        containment=IDummy, renderer=nr)
        wrapper = self._getViewCallable(config)
@@ -1574,11 +1807,12 @@
        self.assertEqual(result.settings, settings)
    def test_add_view_with_request_type_as_iface(self):
        from pyramid.renderers import null_renderer
        from zope.interface import directlyProvides
        def view(context, request):
            return 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(request_type=IDummy, view=view)
        config.add_view(request_type=IDummy, view=view, renderer=null_renderer)
        wrapper = self._getViewCallable(config, None)
        request = self._makeRequest(config)
        directlyProvides(request, IDummy)
@@ -1593,10 +1827,11 @@
                          config.add_view, view, '', None, None, object)
    def test_add_view_with_route_name(self):
        from pyramid.renderers import null_renderer
        from zope.component import ComponentLookupError
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, route_name='foo')
        config.add_view(view=view, route_name='foo', renderer=null_renderer)
        self.assertEqual(len(config.registry.deferred_route_views), 1)
        infos = config.registry.deferred_route_views['foo']
        self.assertEqual(len(infos), 1)
@@ -1636,11 +1871,13 @@
        self.assertEqual(info['custom_predicates'], ('123',))
    def test_add_view_with_route_name_exception(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from zope.component import ComponentLookupError
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, route_name='foo', context=RuntimeError)
        config.add_view(view=view, route_name='foo', context=RuntimeError,
                        renderer=null_renderer)
        self.assertEqual(len(config.registry.deferred_route_views), 1)
        infos = config.registry.deferred_route_views['foo']
        self.assertEqual(len(infos), 1)
@@ -1667,9 +1904,11 @@
        self.assertEqual(wrapper_exc_view(None, None), 'OK')
    def test_add_view_with_request_method_true(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, request_method='POST')
        config.add_view(view=view, request_method='POST',
                        renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        request.method = 'POST'
@@ -1685,9 +1924,10 @@
        self._assertNotFound(wrapper, None, request)
    def test_add_view_with_request_param_noval_true(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, request_param='abc')
        config.add_view(view=view, request_param='abc', renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        request.params = {'abc':''}
@@ -1703,9 +1943,11 @@
        self._assertNotFound(wrapper, None, request)
    def test_add_view_with_request_param_val_true(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, request_param='abc=123')
        config.add_view(view=view, request_param='abc=123',
                        renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        request.params = {'abc':'123'}
@@ -1721,9 +1963,10 @@
        self._assertNotFound(wrapper, None, request)
    def test_add_view_with_xhr_true(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, xhr=True)
        config.add_view(view=view, xhr=True, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        request.is_xhr = True
@@ -1746,9 +1989,10 @@
                          config.add_view, view=view, header='Host:a\\')
    def test_add_view_with_header_noval_match(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, header='Host')
        config.add_view(view=view, header='Host', renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        request.headers = {'Host':'whatever'}
@@ -1764,9 +2008,10 @@
        self._assertNotFound(wrapper, None, request)
    def test_add_view_with_header_val_match(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, header=r'Host:\d')
        config.add_view(view=view, header=r'Host:\d', renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        request.headers = {'Host':'1'}
@@ -1792,9 +2037,10 @@
        self.assertRaises(HTTPNotFound, wrapper, None, request)
    def test_add_view_with_accept_match(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, accept='text/xml')
        config.add_view(view=view, accept='text/xml', renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        request.accept = ['text/xml']
@@ -1810,10 +2056,11 @@
        self._assertNotFound(wrapper, None, request)
    def test_add_view_with_containment_true(self):
        from pyramid.renderers import null_renderer
        from zope.interface import directlyProvides
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, containment=IDummy)
        config.add_view(view=view, containment=IDummy, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        context = DummyContext()
        directlyProvides(context, IDummy)
@@ -1828,12 +2075,14 @@
        self._assertNotFound(wrapper, context, None)
    def test_add_view_with_containment_dottedname(self):
        from pyramid.renderers import null_renderer
        from zope.interface import directlyProvides
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(
            view=view,
            containment='pyramid.tests.test_config.IDummy')
            containment='pyramid.tests.test_config.IDummy',
            renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        context = DummyContext()
        directlyProvides(context, IDummy)
@@ -1847,9 +2096,10 @@
                          config.add_view, view=view, path_info='\\')
    def test_add_view_with_path_info_match(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, path_info='/foo')
        config.add_view(view=view, path_info='/foo', renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        request.path_info = '/foo'
@@ -1865,6 +2115,7 @@
        self._assertNotFound(wrapper, None, request)
    def test_add_view_with_custom_predicates_match(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        config = self._makeOne(autocommit=True)
        def pred1(context, request):
@@ -1872,7 +2123,8 @@
        def pred2(context, request):
            return True
        predicates = (pred1, pred2)
        config.add_view(view=view, custom_predicates=predicates)
        config.add_view(view=view, custom_predicates=predicates,
                        renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        self.assertEqual(wrapper(None, request), 'OK')
@@ -1891,24 +2143,30 @@
        self._assertNotFound(wrapper, None, request)
    def test_add_view_custom_predicate_bests_standard_predicate(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        view2 = lambda *arg: 'NOT OK'
        config = self._makeOne(autocommit=True)
        def pred1(context, request):
            return True
        config.add_view(view=view, custom_predicates=(pred1,))
        config.add_view(view=view2, request_method='GET')
        config.add_view(view=view, custom_predicates=(pred1,),
                        renderer=null_renderer)
        config.add_view(view=view2, request_method='GET',
                        renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        request.method = 'GET'
        self.assertEqual(wrapper(None, request), 'OK')
    def test_add_view_custom_more_preds_first_bests_fewer_preds_last(self):
        from pyramid.renderers import null_renderer
        view = lambda *arg: 'OK'
        view2 = lambda *arg: 'NOT OK'
        config = self._makeOne(autocommit=True)
        config.add_view(view=view, request_method='GET', xhr=True)
        config.add_view(view=view2, request_method='GET')
        config.add_view(view=view, request_method='GET', xhr=True,
                        renderer=null_renderer)
        config.add_view(view=view2, request_method='GET',
                        renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        request = self._makeRequest(config)
        request.method = 'GET'
@@ -1925,6 +2183,7 @@
        self.assertRaises(ConfigurationConflictError, config.commit)
    def test_add_view_with_permission(self):
        from pyramid.renderers import null_renderer
        view1 = lambda *arg: 'OK'
        outerself = self
        class DummyPolicy(object):
@@ -1940,12 +2199,13 @@
        config = self._makeOne(authorization_policy=policy,
                               authentication_policy=policy,
                               autocommit=True)
        config.add_view(view=view1, permission='view')
        config.add_view(view=view1, permission='view', renderer=null_renderer)
        view = self._getViewCallable(config)
        request = self._makeRequest(config)
        self.assertEqual(view(None, request), 'OK')
    def test_add_view_with_default_permission_no_explicit_permission(self):
        from pyramid.renderers import null_renderer
        view1 = lambda *arg: 'OK'
        outerself = self
        class DummyPolicy(object):
@@ -1962,19 +2222,20 @@
                               authentication_policy=policy,
                               default_permission='view',
                               autocommit=True)
        config.add_view(view=view1)
        config.add_view(view=view1, renderer=null_renderer)
        view = self._getViewCallable(config)
        request = self._makeRequest(config)
        self.assertEqual(view(None, request), 'OK')
    def test_add_view_with_no_default_permission_no_explicit_permission(self):
        from pyramid.renderers import null_renderer
        view1 = lambda *arg: 'OK'
        class DummyPolicy(object): pass # wont be called
        policy = DummyPolicy()
        config = self._makeOne(authorization_policy=policy,
                               authentication_policy=policy,
                               autocommit=True)
        config.add_view(view=view1)
        config.add_view(view=view1, renderer=null_renderer)
        view = self._getViewCallable(config)
        request = self._makeRequest(config)
        self.assertEqual(view(None, request), 'OK')
@@ -2017,6 +2278,13 @@
        self.assertEqual(route.pattern, 'root/path')
        self._assertRoute(config, 'name', 'root/path')
    def test_add_route_discriminator(self):
        config = self._makeOne()
        route = config.add_route('name', 'path')
        self._assertRoute(config, 'name', 'path')
        self.assertEqual(route.name, 'name')
        self.assertEqual(config._ctx.actions[-1][0], ('route', 'name'))
    def test_add_route_with_factory(self):
        config = self._makeOne(autocommit=True)
@@ -2176,17 +2444,19 @@
            raise AssertionError
    def test_derive_view_function(self):
        from pyramid.renderers import null_renderer
        def view(request):
            return 'OK'
        config = self._makeOne()
        result = config.derive_view(view)
        result = config.derive_view(view, renderer=null_renderer)
        self.assertFalse(result is view)
        self.assertEqual(result(None, None), 'OK')
    def test_derive_view_dottedname(self):
        from pyramid.renderers import null_renderer
        config = self._makeOne()
        result = config.derive_view(
            'pyramid.tests.test_config.dummy_view')
            'pyramid.tests.test_config.dummy_view', renderer=null_renderer)
        self.assertFalse(result is dummy_view)
        self.assertEqual(result(None, None), 'OK')
@@ -2279,18 +2549,21 @@
        self.assertEqual(config._ctx.info, 'abc')
    def test_add_static_here_no_utility_registered(self):
        from pyramid.renderers import null_renderer
        from zope.interface import Interface
        from pyramid.static import PackageURLParser
        from pyramid.interfaces import IView
        from pyramid.interfaces import IViewClassifier
        config = self._makeOne(autocommit=True)
        config.add_static_view('static', 'fixtures/static')
        config.add_static_view('static', 'fixtures/static',
                               renderer=null_renderer)
        request_type = self._getRouteRequestIface(config, 'static/')
        self._assertRoute(config, 'static/', 'static/*subpath')
        wrapped = config.registry.adapters.lookup(
            (IViewClassifier, request_type, Interface), IView, name='')
        request = self._makeRequest(config)
        self.assertEqual(wrapped(None, request).__class__, PackageURLParser)
        result = wrapped(None, request)
        self.assertEqual(result.__class__, PackageURLParser)
    def test_add_static_view_package_relative(self):
        from pyramid.interfaces import IStaticURLInfo
@@ -2323,12 +2596,13 @@
                         [('static', static_path, {})])
    def test_set_notfound_view(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.httpexceptions import HTTPNotFound
        config = self._makeOne(autocommit=True)
        view = lambda *arg: arg
        config.set_notfound_view(view)
        config.set_notfound_view(view, renderer=null_renderer)
        request = self._makeRequest(config)
        view = self._getViewCallable(config,
                                     ctx_iface=implementedBy(HTTPNotFound),
@@ -2337,12 +2611,13 @@
        self.assertEqual(result, (None, request))
    def test_set_notfound_view_request_has_context(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.httpexceptions import HTTPNotFound
        config = self._makeOne(autocommit=True)
        view = lambda *arg: arg
        config.set_notfound_view(view)
        config.set_notfound_view(view, renderer=null_renderer)
        request = self._makeRequest(config)
        request.context = 'abc'
        view = self._getViewCallable(config,
@@ -2372,12 +2647,13 @@
        self.assertTrue('div' in result.body)
    def test_set_forbidden_view(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.httpexceptions import HTTPForbidden
        config = self._makeOne(autocommit=True)
        view = lambda *arg: 'OK'
        config.set_forbidden_view(view)
        config.set_forbidden_view(view, renderer=null_renderer)
        request = self._makeRequest(config)
        view = self._getViewCallable(config,
                                     ctx_iface=implementedBy(HTTPForbidden),
@@ -2386,12 +2662,13 @@
        self.assertEqual(result, 'OK')
    def test_set_forbidden_view_request_has_context(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        from pyramid.interfaces import IRequest
        from pyramid.httpexceptions import HTTPForbidden
        config = self._makeOne(autocommit=True)
        view = lambda *arg: arg
        config.set_forbidden_view(view)
        config.set_forbidden_view(view, renderer=null_renderer)
        request = self._makeRequest(config)
        request.context = 'abc'
        view = self._getViewCallable(config,
@@ -2870,6 +3147,11 @@
        result = render_view_to_response(ctx, req, '')
        self.assertEqual(result, 'grokked')
    def test_scan_integration_with_extra_kw(self):
        config = self._makeOne(autocommit=True)
        config.scan('pyramid.tests.venusianapp', a=1)
        self.assertEqual(config.a, 1)
    def test_testing_securitypolicy(self):
        from pyramid.testing import DummySecurityPolicy
        config = self._makeOne(autocommit=True)
@@ -3111,13 +3393,14 @@
        self.assertEqual(registeredview.__name__, 'view3')
    def test_autocommit_no_conflicts(self):
        from pyramid.renderers import null_renderer
        config = self._makeOne(autocommit=True)
        def view1(request): pass
        def view2(request): pass
        def view3(request): pass
        config.add_view(view1)
        config.add_view(view2)
        config.add_view(view3)
        config.add_view(view1, renderer=null_renderer)
        config.add_view(view2, renderer=null_renderer)
        config.add_view(view3, renderer=null_renderer)
        config.commit()
        registeredview = self._getViewCallable(config)
        self.assertEqual(registeredview.__name__, 'view3')
@@ -3212,8 +3495,10 @@
    def _makeOne(self, *arg, **kw):
        from pyramid.config import Configurator
        return Configurator(*arg, **kw)
        config = Configurator(*arg, **kw)
        config.registry._dont_resolve_responses = True
        return config
    def _getRouteRequestIface(self, config, name):
        from pyramid.interfaces import IRouteRequest
        iface = config.registry.getUtility(IRouteRequest, name)
@@ -3271,18 +3556,21 @@
                yield confinst[2]
    def test_add_route_with_view(self):
        from pyramid.renderers import null_renderer
        config = self._makeOne(autocommit=True)
        view = lambda *arg: 'OK'
        config.add_route('name', 'path', view=view)
        config.add_route('name', 'path', view=view, view_renderer=null_renderer)
        request_type = self._getRouteRequestIface(config, 'name')
        wrapper = self._getViewCallable(config, None, request_type)
        self.assertEqual(wrapper(None, None), 'OK')
        self._assertRoute(config, 'name', 'path')
    def test_add_route_with_view_context(self):
        from pyramid.renderers import null_renderer
        config = self._makeOne(autocommit=True)
        view = lambda *arg: 'OK'
        config.add_route('name', 'path', view=view, view_context=IDummy)
        config.add_route('name', 'path', view=view, view_context=IDummy,
                         view_renderer=null_renderer)
        request_type = self._getRouteRequestIface(config, 'name')
        wrapper = self._getViewCallable(config, IDummy, request_type)
        self.assertEqual(wrapper(None, None), 'OK')
@@ -3291,10 +3579,12 @@
        self.assertEqual(wrapper, None)
    def test_add_route_with_view_exception(self):
        from pyramid.renderers import null_renderer
        from zope.interface import implementedBy
        config = self._makeOne(autocommit=True)
        view = lambda *arg: 'OK'
        config.add_route('name', 'path', view=view, view_context=RuntimeError)
        config.add_route('name', 'path', view=view, view_context=RuntimeError,
                         view_renderer=null_renderer)
        request_type = self._getRouteRequestIface(config, 'name')
        wrapper = self._getViewCallable(
            config, ctx_iface=implementedBy(RuntimeError),
@@ -3307,9 +3597,11 @@
        self.assertEqual(wrapper, None)
    def test_add_route_with_view_for(self):
        from pyramid.renderers import null_renderer
        config = self._makeOne(autocommit=True)
        view = lambda *arg: 'OK'
        config.add_route('name', 'path', view=view, view_for=IDummy)
        config.add_route('name', 'path', view=view, view_for=IDummy,
                         view_renderer=null_renderer)
        request_type = self._getRouteRequestIface(config, 'name')
        wrapper = self._getViewCallable(config, IDummy, request_type)
        self.assertEqual(wrapper(None, None), 'OK')
@@ -3318,9 +3610,11 @@
        self.assertEqual(wrapper, None)
    def test_add_route_with_for_(self):
        from pyramid.renderers import null_renderer
        config = self._makeOne(autocommit=True)
        view = lambda *arg: 'OK'
        config.add_route('name', 'path', view=view, for_=IDummy)
        config.add_route('name', 'path', view=view, for_=IDummy,
                         view_renderer=null_renderer)
        request_type = self._getRouteRequestIface(config, 'name')
        wrapper = self._getViewCallable(config, IDummy, request_type)
        self.assertEqual(wrapper(None, None), 'OK')
@@ -3340,6 +3634,7 @@
        self.assertEqual(wrapper(None, None).body, 'Hello!')
    def test_add_route_with_view_attr(self):
        from pyramid.renderers import null_renderer
        config = self._makeOne(autocommit=True)
        self._registerRenderer(config)
        class View(object):
@@ -3347,7 +3642,8 @@
                pass
            def alt(self):
                return 'OK'
        config.add_route('name', 'path', view=View, view_attr='alt')
        config.add_route('name', 'path', view=View, view_attr='alt',
                         view_renderer=null_renderer)
        request_type = self._getRouteRequestIface(config, 'name')
        wrapper = self._getViewCallable(config, None, request_type)
        self._assertRoute(config, 'name', 'path')
@@ -3520,29 +3816,31 @@
        self.config.registry.registerUtility(policy, IAuthorizationPolicy)
    def test_requestonly_function(self):
        response = DummyResponse()
        def view(request):
            return 'OK'
            return response
        deriver = self._makeOne()
        result = deriver(view)
        self.assertFalse(result is view)
        self.assertEqual(result(None, None), 'OK')
        self.assertEqual(result(None, None), response)
    def test_requestonly_function_with_renderer(self):
        response = DummyResponse()
        class moo(object):
            def render_view(inself, req, resp, view_inst, ctx):
                self.assertEqual(req, request)
                self.assertEqual(resp, 'OK')
                self.assertEqual(view_inst, view)
                self.assertEqual(ctx, context)
                return 'moo'
                return response
        def view(request):
            return 'OK'
        deriver = self._makeOne(renderer=moo())
        result = deriver(view)
        self.assertFalse(result is view)
        self.assertFalse(result.__wraps__ is view)
        request = self._makeRequest()
        context = testing.DummyResource()
        self.assertEqual(result(context, request), 'moo')
        self.assertEqual(result(context, request), response)
    def test_requestonly_function_with_renderer_request_override(self):
        def moo(info):
@@ -3564,46 +3862,50 @@
        self.assertEqual(result(context, request).body, 'moo')
    def test_requestonly_function_with_renderer_request_has_view(self):
        response = DummyResponse()
        class moo(object):
            def render_view(inself, req, resp, view_inst, ctx):
                self.assertEqual(req, request)
                self.assertEqual(resp, 'OK')
                self.assertEqual(view_inst, 'view')
                self.assertEqual(ctx, context)
                return 'moo'
                return response
        def view(request):
            return 'OK'
        deriver = self._makeOne(renderer=moo())
        result = deriver(view)
        self.assertFalse(result is view)
        self.assertFalse(result.__wraps__ is view)
        request = self._makeRequest()
        request.__view__ = 'view'
        context = testing.DummyResource()
        self.assertEqual(result(context, request), 'moo')
        r = result(context, request)
        self.assertEqual(r, response)
        self.assertFalse(hasattr(request, '__view__'))
    def test_class_without_attr(self):
        response = DummyResponse()
        class View(object):
            def __init__(self, request):
                pass
            def __call__(self):
                return 'OK'
                return response
        deriver = self._makeOne()
        result = deriver(View)
        request = self._makeRequest()
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(request.__view__.__class__, View)
    def test_class_with_attr(self):
        response = DummyResponse()
        class View(object):
            def __init__(self, request):
                pass
            def another(self):
                return 'OK'
                return response
        deriver = self._makeOne(attr='another')
        result = deriver(View)
        request = self._makeRequest()
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(request.__view__.__class__, View)
    def test_as_function_context_and_request(self):
@@ -3611,13 +3913,14 @@
            return 'OK'
        deriver = self._makeOne()
        result = deriver(view)
        self.assertTrue(result is view)
        self.assertTrue(result.__wraps__ is view)
        self.assertFalse(hasattr(result, '__call_permissive__'))
        self.assertEqual(view(None, None), 'OK')
    def test_as_function_requestonly(self):
        response = DummyResponse()
        def view(request):
            return 'OK'
            return response
        deriver = self._makeOne()
        result = deriver(view)
        self.assertFalse(result is view)
@@ -3625,14 +3928,15 @@
        self.assertEqual(view.__doc__, result.__doc__)
        self.assertEqual(view.__name__, result.__name__)
        self.assertFalse(hasattr(result, '__call_permissive__'))
        self.assertEqual(result(None, None), 'OK')
        self.assertEqual(result(None, None), response)
    def test_as_newstyle_class_context_and_request(self):
        response = DummyResponse()
        class view(object):
            def __init__(self, context, request):
                pass
            def __call__(self):
                return 'OK'
                return response
        deriver = self._makeOne()
        result = deriver(view)
        self.assertFalse(result is view)
@@ -3641,15 +3945,16 @@
        self.assertEqual(view.__name__, result.__name__)
        self.assertFalse(hasattr(result, '__call_permissive__'))
        request = self._makeRequest()
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(request.__view__.__class__, view)
    def test_as_newstyle_class_requestonly(self):
        response = DummyResponse()
        class view(object):
            def __init__(self, context, request):
                pass
            def __call__(self):
                return 'OK'
                return response
        deriver = self._makeOne()
        result = deriver(view)
        self.assertFalse(result is view)
@@ -3658,15 +3963,16 @@
        self.assertEqual(view.__name__, result.__name__)
        self.assertFalse(hasattr(result, '__call_permissive__'))
        request = self._makeRequest()
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(request.__view__.__class__, view)
    def test_as_oldstyle_class_context_and_request(self):
        response = DummyResponse()
        class view:
            def __init__(self, context, request):
                pass
            def __call__(self):
                return 'OK'
                return response
        deriver = self._makeOne()
        result = deriver(view)
        self.assertFalse(result is view)
@@ -3675,15 +3981,16 @@
        self.assertEqual(view.__name__, result.__name__)
        self.assertFalse(hasattr(result, '__call_permissive__'))
        request = self._makeRequest()
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(request.__view__.__class__, view)
    def test_as_oldstyle_class_requestonly(self):
        response = DummyResponse()
        class view:
            def __init__(self, context, request):
                pass
            def __call__(self):
                return 'OK'
                return response
        deriver = self._makeOne()
        result = deriver(view)
        self.assertFalse(result is view)
@@ -3692,24 +3999,26 @@
        self.assertEqual(view.__name__, result.__name__)
        self.assertFalse(hasattr(result, '__call_permissive__'))
        request = self._makeRequest()
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(request.__view__.__class__, view)
    def test_as_instance_context_and_request(self):
        response = DummyResponse()
        class View:
            def __call__(self, context, request):
                return 'OK'
                return response
        view = View()
        deriver = self._makeOne()
        result = deriver(view)
        self.assertTrue(result is view)
        self.assertTrue(result.__wraps__ is view)
        self.assertFalse(hasattr(result, '__call_permissive__'))
        self.assertEqual(result(None, None), 'OK')
        self.assertEqual(result(None, None), response)
    def test_as_instance_requestonly(self):
        response = DummyResponse()
        class View:
            def __call__(self, request):
                return 'OK'
                return response
        view = View()
        deriver = self._makeOne()
        result = deriver(view)
@@ -3718,10 +4027,11 @@
        self.assertEqual(view.__doc__, result.__doc__)
        self.assertTrue('instance' in result.__name__)
        self.assertFalse(hasattr(result, '__call_permissive__'))
        self.assertEqual(result(None, None), 'OK')
        self.assertEqual(result(None, None), response)
    def test_with_debug_authorization_no_authpol(self):
        view = lambda *arg: 'OK'
        response = DummyResponse()
        view = lambda *arg: response
        self.config.registry.settings = dict(
            debug_authorization=True, reload_templates=True)
        logger = self._registerLogger()
@@ -3734,7 +4044,7 @@
        request = self._makeRequest()
        request.view_name = 'view_name'
        request.url = 'url'
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(len(logger.messages), 1)
        self.assertEqual(logger.messages[0],
                         "debug_authorization of url url (view name "
@@ -3742,7 +4052,8 @@
                         "(no authorization policy in use)")
    def test_with_debug_authorization_authn_policy_no_authz_policy(self):
        view = lambda *arg: 'OK'
        response = DummyResponse()
        view = lambda *arg: response
        self.config.registry.settings = dict(debug_authorization=True)
        from pyramid.interfaces import IAuthenticationPolicy
        policy = DummySecurityPolicy(False)
@@ -3757,7 +4068,7 @@
        request = self._makeRequest()
        request.view_name = 'view_name'
        request.url = 'url'
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(len(logger.messages), 1)
        self.assertEqual(logger.messages[0],
                         "debug_authorization of url url (view name "
@@ -3765,7 +4076,8 @@
                         "(no authorization policy in use)")
    def test_with_debug_authorization_authz_policy_no_authn_policy(self):
        view = lambda *arg: 'OK'
        response = DummyResponse()
        view = lambda *arg: response
        self.config.registry.settings = dict(debug_authorization=True)
        from pyramid.interfaces import IAuthorizationPolicy
        policy = DummySecurityPolicy(False)
@@ -3780,7 +4092,7 @@
        request = self._makeRequest()
        request.view_name = 'view_name'
        request.url = 'url'
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(len(logger.messages), 1)
        self.assertEqual(logger.messages[0],
                         "debug_authorization of url url (view name "
@@ -3788,7 +4100,8 @@
                         "(no authorization policy in use)")
    def test_with_debug_authorization_no_permission(self):
        view = lambda *arg: 'OK'
        response = DummyResponse()
        view = lambda *arg: response
        self.config.registry.settings = dict(
            debug_authorization=True, reload_templates=True)
        self._registerSecurityPolicy(True)
@@ -3802,7 +4115,7 @@
        request = self._makeRequest()
        request.view_name = 'view_name'
        request.url = 'url'
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(len(logger.messages), 1)
        self.assertEqual(logger.messages[0],
                         "debug_authorization of url url (view name "
@@ -3810,7 +4123,8 @@
                         "no permission registered)")
    def test_debug_auth_permission_authpol_permitted(self):
        view = lambda *arg: 'OK'
        response = DummyResponse()
        view = lambda *arg: response
        self.config.registry.settings = dict(
            debug_authorization=True, reload_templates=True)
        logger = self._registerLogger()
@@ -3820,18 +4134,19 @@
        self.assertEqual(view.__module__, result.__module__)
        self.assertEqual(view.__doc__, result.__doc__)
        self.assertEqual(view.__name__, result.__name__)
        self.assertEqual(result.__call_permissive__, view)
        self.assertEqual(result.__call_permissive__.__wraps__, view)
        request = self._makeRequest()
        request.view_name = 'view_name'
        request.url = 'url'
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(len(logger.messages), 1)
        self.assertEqual(logger.messages[0],
                         "debug_authorization of url url (view name "
                         "'view_name' against context None): True")
    def test_debug_auth_permission_authpol_permitted_no_request(self):
        view = lambda *arg: 'OK'
        response = DummyResponse()
        view = lambda *arg: response
        self.config.registry.settings = dict(
            debug_authorization=True, reload_templates=True)
        logger = self._registerLogger()
@@ -3841,8 +4156,8 @@
        self.assertEqual(view.__module__, result.__module__)
        self.assertEqual(view.__doc__, result.__doc__)
        self.assertEqual(view.__name__, result.__name__)
        self.assertEqual(result.__call_permissive__, view)
        self.assertEqual(result(None, None), 'OK')
        self.assertEqual(result.__call_permissive__.__wraps__, view)
        self.assertEqual(result(None, None), response)
        self.assertEqual(len(logger.messages), 1)
        self.assertEqual(logger.messages[0],
                         "debug_authorization of url None (view name "
@@ -3850,7 +4165,8 @@
    def test_debug_auth_permission_authpol_denied(self):
        from pyramid.httpexceptions import HTTPForbidden
        view = lambda *arg: 'OK'
        response = DummyResponse()
        view = lambda *arg: response
        self.config.registry.settings = dict(
            debug_authorization=True, reload_templates=True)
        logger = self._registerLogger()
@@ -3860,7 +4176,7 @@
        self.assertEqual(view.__module__, result.__module__)
        self.assertEqual(view.__doc__, result.__doc__)
        self.assertEqual(view.__name__, result.__name__)
        self.assertEqual(result.__call_permissive__, view)
        self.assertEqual(result.__call_permissive__.__wraps__, view)
        request = self._makeRequest()
        request.view_name = 'view_name'
        request.url = 'url'
@@ -3888,12 +4204,14 @@
        self.assertEqual(permitted, False)
    def test_debug_auth_permission_authpol_overridden(self):
        view = lambda *arg: 'OK'
        from pyramid.security import NO_PERMISSION_REQUIRED
        response = DummyResponse()
        view = lambda *arg: response
        self.config.registry.settings = dict(
            debug_authorization=True, reload_templates=True)
        logger = self._registerLogger()
        self._registerSecurityPolicy(False)
        deriver = self._makeOne(permission='__no_permission_required__')
        deriver = self._makeOne(permission=NO_PERMISSION_REQUIRED)
        result = deriver(view)
        self.assertEqual(view.__module__, result.__module__)
        self.assertEqual(view.__doc__, result.__doc__)
@@ -3902,14 +4220,15 @@
        request = self._makeRequest()
        request.view_name = 'view_name'
        request.url = 'url'
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
        self.assertEqual(len(logger.messages), 1)
        self.assertEqual(logger.messages[0],
                         "debug_authorization of url url (view name "
                         "'view_name' against context None): False")
    def test_secured_view_authn_policy_no_authz_policy(self):
        view = lambda *arg: 'OK'
        response = DummyResponse()
        view = lambda *arg: response
        self.config.registry.settings = {}
        from pyramid.interfaces import IAuthenticationPolicy
        policy = DummySecurityPolicy(False)
@@ -3923,10 +4242,11 @@
        request = self._makeRequest()
        request.view_name = 'view_name'
        request.url = 'url'
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
    def test_secured_view_authz_policy_no_authn_policy(self):
        view = lambda *arg: 'OK'
        response = DummyResponse()
        view = lambda *arg: response
        self.config.registry.settings = {}
        from pyramid.interfaces import IAuthorizationPolicy
        policy = DummySecurityPolicy(False)
@@ -3940,10 +4260,11 @@
        request = self._makeRequest()
        request.view_name = 'view_name'
        request.url = 'url'
        self.assertEqual(result(None, request), 'OK')
        self.assertEqual(result(None, request), response)
    def test_with_predicates_all(self):
        view = lambda *arg: 'OK'
        response = DummyResponse()
        view = lambda *arg: response
        predicates = []
        def predicate1(context, request):
            predicates.append(True)
@@ -3956,7 +4277,7 @@
        request = self._makeRequest()
        request.method = 'POST'
        next = result(None, None)
        self.assertEqual(next, 'OK')
        self.assertEqual(next, response)
        self.assertEqual(predicates, [True, True])
    def test_with_predicates_checker(self):
@@ -3994,7 +4315,7 @@
        self.assertEqual(predicates, [True, True])
    def test_with_wrapper_viewname(self):
        from webob import Response
        from pyramid.response import Response
        from pyramid.interfaces import IView
        from pyramid.interfaces import IViewClassifier
        inner_response = Response('OK')
@@ -4003,7 +4324,8 @@
        def outer_view(context, request):
            self.assertEqual(request.wrapped_response, inner_response)
            self.assertEqual(request.wrapped_body, inner_response.body)
            self.assertEqual(request.wrapped_view, inner_view)
            self.assertEqual(request.wrapped_view.__original_view__,
                             inner_view)
            return Response('outer ' + request.wrapped_body)
        self.config.registry.registerAdapter(
            outer_view, (IViewClassifier, None, None), IView, 'owrap')
@@ -4018,7 +4340,7 @@
        self.assertEqual(response.body, 'outer OK')
    def test_with_wrapper_viewname_notfound(self):
        from webob import Response
        from pyramid.response import Response
        inner_response = Response('OK')
        def inner_view(context, request):
            return inner_response
@@ -4028,13 +4350,14 @@
        self.assertRaises(ValueError, wrapped, None, request)
    def test_as_newstyle_class_context_and_request_attr_and_renderer(self):
        response = DummyResponse()
        class renderer(object):
            def render_view(inself, req, resp, view_inst, ctx):
                self.assertEqual(req, request)
                self.assertEqual(resp, {'a':'1'})
                self.assertEqual(view_inst.__class__, View)
                self.assertEqual(ctx, context)
                return resp
                return response
        class View(object):
            def __init__(self, context, request):
                pass
@@ -4048,16 +4371,17 @@
        self.assertEqual(result.__name__, View.__name__)
        request = self._makeRequest()
        context = testing.DummyResource()
        self.assertEqual(result(context, request), {'a':'1'})
        self.assertEqual(result(context, request), response)
    def test_as_newstyle_class_requestonly_attr_and_renderer(self):
        response = DummyResponse()
        class renderer(object):
            def render_view(inself, req, resp, view_inst, ctx):
                self.assertEqual(req, request)
                self.assertEqual(resp, {'a':'1'})
                self.assertEqual(view_inst.__class__, View)
                self.assertEqual(ctx, context)
                return resp
                return response
        class View(object):
            def __init__(self, request):
                pass
@@ -4071,16 +4395,17 @@
        self.assertEqual(result.__name__, View.__name__)
        request = self._makeRequest()
        context = testing.DummyResource()
        self.assertEqual(result(context, request), {'a':'1'})
        self.assertEqual(result(context, request), response)
    def test_as_oldstyle_cls_context_request_attr_and_renderer(self):
        response = DummyResponse()
        class renderer(object):
            def render_view(inself, req, resp, view_inst, ctx):
                self.assertEqual(req, request)
                self.assertEqual(resp, {'a':'1'})
                self.assertEqual(view_inst.__class__, View)
                self.assertEqual(ctx, context)
                return resp
                return response
        class View:
            def __init__(self, context, request):
                pass
@@ -4094,16 +4419,17 @@
        self.assertEqual(result.__name__, View.__name__)
        request = self._makeRequest()
        context = testing.DummyResource()
        self.assertEqual(result(context, request), {'a':'1'})
        self.assertEqual(result(context, request), response)
    def test_as_oldstyle_cls_requestonly_attr_and_renderer(self):
        response = DummyResponse()
        class renderer(object):
            def render_view(inself, req, resp, view_inst, ctx):
                self.assertEqual(req, request)
                self.assertEqual(resp, {'a':'1'})
                self.assertEqual(view_inst.__class__, View)
                self.assertEqual(ctx, context)
                return resp
                return response
        class View:
            def __init__(self, request):
                pass
@@ -4117,16 +4443,17 @@
        self.assertEqual(result.__name__, View.__name__)
        request = self._makeRequest()
        context = testing.DummyResource()
        self.assertEqual(result(context, request), {'a':'1'})
        self.assertEqual(result(context, request), response)
    def test_as_instance_context_and_request_attr_and_renderer(self):
        response = DummyResponse()
        class renderer(object):
            def render_view(inself, req, resp, view_inst, ctx):
                self.assertEqual(req, request)
                self.assertEqual(resp, {'a':'1'})
                self.assertEqual(view_inst, view)
                self.assertEqual(ctx, context)
                return resp
                return response
        class View:
            def index(self, context, request):
                return {'a':'1'}
@@ -4138,16 +4465,17 @@
        self.assertEqual(result.__doc__, view.__doc__)
        request = self._makeRequest()
        context = testing.DummyResource()
        self.assertEqual(result(context, request), {'a':'1'})
        self.assertEqual(result(context, request), response)
    def test_as_instance_requestonly_attr_and_renderer(self):
        response = DummyResponse()
        class renderer(object):
            def render_view(inself, req, resp, view_inst, ctx):
                self.assertEqual(req, request)
                self.assertEqual(resp, {'a':'1'})
                self.assertEqual(view_inst, view)
                self.assertEqual(ctx, context)
                return resp
                return response
        class View:
            def index(self, request):
                return {'a':'1'}
@@ -4159,58 +4487,63 @@
        self.assertEqual(result.__doc__, view.__doc__)
        request = self._makeRequest()
        context = testing.DummyResource()
        self.assertEqual(result(context, request), {'a':'1'})
        self.assertEqual(result(context, request), response)
    def test_with_view_mapper_config_specified(self):
        response = DummyResponse()
        class mapper(object):
            def __init__(self, **kw):
                self.kw = kw
            def __call__(self, view):
                def wrapped(context, request):
                    return 'OK'
                    return response
                return wrapped
        def view(context, request): return 'NOTOK'
        deriver = self._makeOne(mapper=mapper)
        result = deriver(view)
        self.assertFalse(result is view)
        self.assertEqual(result(None, None), 'OK')
        self.assertFalse(result.__wraps__ is view)
        self.assertEqual(result(None, None), response)
    def test_with_view_mapper_view_specified(self):
        from pyramid.response import Response
        response = Response()
        def mapper(**kw):
            def inner(view):
                def superinner(context, request):
                    self.assertEqual(request, None)
                    return 'OK'
                    return response
                return superinner
            return inner
        def view(context, request): return 'NOTOK'
        view.__view_mapper__ = mapper
        deriver = self._makeOne()
        result = deriver(view)
        self.assertFalse(result is view)
        self.assertEqual(result(None, None), 'OK')
        self.assertFalse(result.__wraps__ is view)
        self.assertEqual(result(None, None), response)
    def test_with_view_mapper_default_mapper_specified(self):
        from pyramid.response import Response
        response = Response()
        def mapper(**kw):
            def inner(view):
                def superinner(context, request):
                    self.assertEqual(request, None)
                    return 'OK'
                    return  response
                return superinner
            return inner
        self.config.set_view_mapper(mapper)
        def view(context, request): return 'NOTOK'
        deriver = self._makeOne()
        result = deriver(view)
        self.assertFalse(result is view)
        self.assertEqual(result(None, None), 'OK')
        self.assertFalse(result.__wraps__ is view)
        self.assertEqual(result(None, None), response)
    def test_attr_wrapped_view_branching_default_phash(self):
        from pyramid.config import DEFAULT_PHASH
        def view(context, request): pass
        deriver = self._makeOne(phash=DEFAULT_PHASH)
        result = deriver(view)
        self.assertEqual(result, view)
        self.assertEqual(result.__wraps__, view)
    def test_attr_wrapped_view_branching_nondefault_phash(self):
        def view(context, request): pass
@@ -4220,7 +4553,7 @@
    def test_http_cached_view_integer(self):
        import datetime
        from webob import Response
        from pyramid.response import Response
        response = Response('OK')
        def inner_view(context, request):
            return response
@@ -4240,7 +4573,7 @@
        
    def test_http_cached_view_timedelta(self):
        import datetime
        from webob import Response
        from pyramid.response import Response
        response = Response('OK')
        def inner_view(context, request):
            return response
@@ -4260,7 +4593,7 @@
    def test_http_cached_view_tuple(self):
        import datetime
        from webob import Response
        from pyramid.response import Response
        response = Response('OK')
        def inner_view(context, request):
            return response
@@ -4279,7 +4612,7 @@
        self.assertEqual(headers['Cache-Control'], 'max-age=3600, public')
    def test_http_cached_view_tuple_seconds_None(self):
        from webob import Response
        from pyramid.response import Response
        response = Response('OK')
        def inner_view(context, request):
            return response
@@ -4295,17 +4628,35 @@
        self.assertFalse('Expires' in headers)
        self.assertEqual(headers['Cache-Control'], 'public')
    def test_http_cached_view_nonresponse_object_returned_downstream(self):
    def test_http_cached_view_prevent_auto_set(self):
        from pyramid.response import Response
        response = Response()
        response.cache_control.prevent_auto = True
        def inner_view(context, request):
            return None
            return response
        deriver = self._makeOne(http_cache=3600)
        result = deriver(inner_view)
        self.assertFalse(result is inner_view)
        self.assertEqual(inner_view.__module__, result.__module__)
        self.assertEqual(inner_view.__doc__, result.__doc__)
        request = self._makeRequest()
        result = result(None, request)
        self.assertEqual(result, None) # doesn't blow up
        self.assertEqual(result, response) # doesn't blow up
        headers = dict(result.headerlist)
        self.assertFalse('Expires' in headers)
        self.assertFalse('Cache-Control' in headers)
    def test_http_cached_prevent_http_cache_in_settings(self):
        self.config.registry.settings['prevent_http_cache'] = True
        from pyramid.response import Response
        response = Response()
        def inner_view(context, request):
            return response
        deriver = self._makeOne(http_cache=3600)
        result = deriver(inner_view)
        request = self._makeRequest()
        result = result(None, request)
        self.assertEqual(result, response)
        headers = dict(result.headerlist)
        self.assertFalse('Expires' in headers)
        self.assertFalse('Cache-Control' in headers)
    def test_http_cached_view_bad_tuple(self):
        from pyramid.exceptions import ConfigurationError
@@ -5392,6 +5743,11 @@
    """ """
def dummy_include(config):
    config.registry.included = True
    config.action('discrim', None, config.package)
def dummy_include2(config):
    config.registry.also_included = True
    config.action('discrim', None, config.package)
includeme = dummy_include
@@ -5419,9 +5775,19 @@
def parse_httpdate(s):
    import datetime
    return datetime.datetime.strptime(s, "%a, %d %b %Y %H:%M:%S %Z")
    # cannot use %Z, must use literal GMT; Jython honors timezone
    # but CPython does not
    return datetime.datetime.strptime(s, "%a, %d %b %Y %H:%M:%S GMT")
def assert_similar_datetime(one, two):
    for attr in ('year', 'month', 'day', 'hour', 'minute'):
        assert(getattr(one, attr) == getattr(two, attr))
        one_attr = getattr(one, attr)
        two_attr = getattr(two, attr)
        if not one_attr == two_attr: # pragma: no cover
            raise AssertionError('%r != %r in %s' % (one_attr, two_attr, attr))
from pyramid.interfaces import IResponse
class DummyResponse(object):
    implements(IResponse)
    
def dummy_tween_factory(handler, registry): pass
pyramid/tests/test_events.py
@@ -179,9 +179,9 @@
                         [(foo, dec.register, 'pyramid')])
class TestBeforeRender(unittest.TestCase):
    def _makeOne(self, system):
    def _makeOne(self, system, val=None):
        from pyramid.events import BeforeRender
        return BeforeRender(system)
        return BeforeRender(system, val)
    def test_instance_conforms(self):
        from zope.interface.verify import verifyObject
@@ -195,21 +195,26 @@
        event['a'] = 1
        self.assertEqual(system, {'a':1})
    def test_setitem_fail(self):
        system = {'a':1}
    def test_setdefault_fail(self):
        system = {}
        event = self._makeOne(system)
        self.assertRaises(KeyError, event.__setitem__, 'a',  1)
        result = event.setdefault('a', 1)
        self.assertEqual(result, 1)
        self.assertEqual(system, {'a':1})
    def test_setdefault_success(self):
        system = {}
        event = self._makeOne(system)
        event['a'] = 1
        result = event.setdefault('a', 2)
        self.assertEqual(result, 1)
        self.assertEqual(system, {'a':1})
    def test_update_success(self):
        system = {'a':1}
        event = self._makeOne(system)
        event.update({'b':2})
        self.assertEqual(system, {'a':1, 'b':2})
    def test_update_fail(self):
        system = {'a':1}
        event = self._makeOne(system)
        self.assertRaises(KeyError, event.update, {'a':1})
    def test__contains__True(self):
        system = {'a':1}
@@ -241,6 +246,11 @@
        event = self._makeOne(system)
        self.assertEqual(event.get('a'), None)
    def test_rendering_val(self):
        system = {}
        val = {}
        event = self._makeOne(system, val)
        self.assertTrue(event.rendering_val is val)
class DummyConfigurator(object):
    def __init__(self):
pyramid/tests/test_httpexceptions.py
@@ -232,6 +232,17 @@
        body = list(exc(environ, start_response))[0]
        self.assertEqual(body, '200 OK\n\nGET')
    def test_custom_body_template_with_custom_variable_doesnt_choke(self):
        cls = self._getTargetSubclass()
        exc = cls(body_template='${REQUEST_METHOD}')
        environ = _makeEnviron()
        class Choke(object):
            def __str__(self): raise ValueError
        environ['gardentheory.user'] = Choke()
        start_response = DummyStartResponse()
        body = list(exc(environ, start_response))[0]
        self.assertEqual(body, '200 OK\n\nGET')
    def test_body_template_unicode(self):
        cls = self._getTargetSubclass()
        la = unicode('/La Pe\xc3\xb1a', 'utf-8')
pyramid/tests/test_i18n.py
@@ -65,6 +65,19 @@
            )
        self.assertTrue(localizer.pluralizer is pluralizer)
    def test_pluralize_default_translations(self):
        # test that even without message ids loaded that
        # "localizer.pluralize" "works" instead of raising an inscrutable
        # "translations object has no attr 'plural' error; see
        # see https://github.com/Pylons/pyramid/issues/235
        from pyramid.i18n import Translations
        translations = Translations()
        translations._catalog = {}
        localizer = self._makeOne(None, translations)
        result = localizer.pluralize('singular', 'plural', 2, domain='1',
                                     mapping={})
        self.assertEqual(result, 'plural')
class Test_negotiate_locale_name(unittest.TestCase):
    def setUp(self):
        cleanUp()
@@ -469,6 +482,12 @@
        self.assertEqual(t.dungettext('messages', 'foo1', 'foos1', 1), 'Voh1')
        self.assertEqual(t.dungettext('messages1', 'foo1', 'foos1', 1), 'VohD1')
    def test_default_germanic_pluralization(self):
        t = self._getTargetClass()()
        t._catalog = {}
        result = t.dungettext('messages', 'foo1', 'foos1', 2)
        self.assertEqual(result, 'foos1')
class DummyRequest(object):
    def __init__(self):
pyramid/tests/test_integration.py
@@ -3,7 +3,7 @@
from pyramid.wsgi import wsgiapp
from pyramid.view import view_config
from pyramid.view import static
from pyramid.static import static_view
from zope.interface import Interface
@@ -39,10 +39,10 @@
        reg = config.registry
        view = reg.adapters.lookup(
            (IViewClassifier, IRequest, INothing), IView, name='')
        self.assertEqual(view, wsgiapptest)
        self.assertEqual(view.__original_view__, wsgiapptest)
here = os.path.dirname(__file__)
staticapp = static(os.path.join(here, 'fixtures'))
staticapp = static_view(os.path.join(here, 'fixtures'), use_subpath=True)
class TestStaticApp(unittest.TestCase):
    def test_basic(self):
pyramid/tests/test_log.py
File was deleted
pyramid/tests/test_paster.py
@@ -5,135 +5,174 @@
        from pyramid.paster import PShellCommand
        return PShellCommand
    def _makeOne(self):
        return self._getTargetClass()('pshell')
    def _makeOne(self, patch_bootstrap=True, patch_config=True,
                 patch_args=True, patch_options=True):
        cmd = self._getTargetClass()('pshell')
        if patch_bootstrap:
            self.bootstrap = DummyBootstrap()
            cmd.bootstrap = (self.bootstrap,)
        if patch_config:
            self.config_factory = DummyConfigParserFactory()
            cmd.ConfigParser = self.config_factory
        if patch_args:
            self.args = ('/foo/bar/myapp.ini#myapp',)
            cmd.args = self.args
        if patch_options:
            class Options(object): pass
            self.options = Options()
            self.options.disable_ipython = True
            cmd.options = self.options
        return cmd
    def test_command_ipshell_is_None_ipython_enabled(self):
    def test_make_default_shell(self):
        command = self._makeOne()
        interact = DummyInteractor()
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython = False
        command.command(IPShell=None)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertTrue(loadapp.relative_to)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['request'].registry, dummy_registry)
        self.assertEqual(interact.local, {'root':dummy_root,
                                          'registry':dummy_registry})
        self.assertTrue(interact.banner)
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
        shell = command.make_default_shell(interact)
        shell({'foo': 'bar'}, 'a help message')
        self.assertEqual(interact.local, {'foo': 'bar'})
        self.assertTrue('a help message' in interact.banner)
    def test_command_ipshell_is_not_None_ipython_disabled(self):
    def test_make_ipython_v0_11_shell(self):
        command = self._makeOne()
        interact = DummyInteractor()
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        class Options(object): pass
        command.options = Options()
        ipshell_factory = DummyIPShellFactory()
        shell = command.make_ipython_v0_11_shell(ipshell_factory)
        shell({'foo': 'bar'}, 'a help message')
        self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'})
        self.assertTrue('a help message' in ipshell_factory.kw['banner2'])
        self.assertTrue(ipshell_factory.shell.called)
    def test_make_ipython_v0_10_shell(self):
        command = self._makeOne()
        ipshell_factory = DummyIPShellFactory()
        shell = command.make_ipython_v0_10_shell(ipshell_factory)
        shell({'foo': 'bar'}, 'a help message')
        self.assertEqual(ipshell_factory.kw['argv'], [])
        self.assertEqual(ipshell_factory.kw['user_ns'], {'foo': 'bar'})
        self.assertTrue('a help message' in ipshell_factory.shell.banner)
        self.assertTrue(ipshell_factory.shell.called)
    def test_command_loads_default_shell(self):
        command = self._makeOne()
        shell = DummyShell()
        command.make_ipython_v0_11_shell = lambda: None
        command.make_ipython_v0_10_shell = lambda: None
        command.make_default_shell = lambda: shell
        command.command()
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(shell.env, {
            'app':self.bootstrap.app, 'root':self.bootstrap.root,
            'registry':self.bootstrap.registry,
            'request':self.bootstrap.request,
            'root_factory':self.bootstrap.root_factory,
        })
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(shell.help)
    def test_command_loads_default_shell_with_ipython_disabled(self):
        command = self._makeOne()
        shell = DummyShell()
        bad_shell = DummyShell()
        command.make_ipython_v0_11_shell = lambda: bad_shell
        command.make_ipython_v0_10_shell = lambda: bad_shell
        command.make_default_shell = lambda: shell
        command.options.disable_ipython = True
        command.command(IPShell='notnone')
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertTrue(loadapp.relative_to)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['request'].registry, dummy_registry)
        self.assertEqual(interact.local, {'root':dummy_root,
                                          'registry':dummy_registry})
        self.assertTrue(interact.banner)
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
        command.command()
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(shell.env, {
            'app':self.bootstrap.app, 'root':self.bootstrap.root,
            'registry':self.bootstrap.registry,
            'request':self.bootstrap.request,
            'root_factory':self.bootstrap.root_factory,
        })
        self.assertEqual(bad_shell.env, {})
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(shell.help)
    def test_command_ipython_enabled(self):
    def test_command_loads_ipython_v0_11(self):
        command = self._makeOne()
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        dummy_shell_factory = DummyIPShellFactory()
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        class Options(object): pass
        command.options = Options()
        shell = DummyShell()
        command.make_ipython_v0_11_shell = lambda: shell
        command.make_ipython_v0_10_shell = lambda: None
        command.make_default_shell = lambda: None
        command.options.disable_ipython = False
        command.command(IPShell=dummy_shell_factory)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertTrue(loadapp.relative_to)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['request'].registry, dummy_registry)
        self.assertEqual(dummy_shell_factory.shell.local_ns,
                         {'root':dummy_root, 'registry':dummy_registry})
        self.assertEqual(dummy_shell_factory.shell.global_ns, {})
        self.assertTrue('\n\n' in dummy_shell_factory.shell.IP.BANNER)
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
        command.command()
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(shell.env, {
            'app':self.bootstrap.app, 'root':self.bootstrap.root,
            'registry':self.bootstrap.registry,
            'request':self.bootstrap.request,
            'root_factory':self.bootstrap.root_factory,
        })
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(shell.help)
    def test_command_get_app_hookable(self):
        from paste.deploy import loadapp
    def test_command_loads_ipython_v0_10(self):
        command = self._makeOne()
        app = DummyApp()
        apped = []
        def get_app(*arg, **kw):
            apped.append((arg, kw))
            return app
        command.get_app = get_app
        interact = DummyInteractor()
        app = DummyApp()
        command.interact = (interact,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython =True
        command.command(IPShell=None)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['request'].registry, dummy_registry)
        self.assertEqual(interact.local, {'root':dummy_root,
                                          'registry':dummy_registry})
        self.assertTrue(interact.banner)
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
        self.assertEqual(apped, [(('/foo/bar/myapp.ini', 'myapp'),
                                  {'loadapp': loadapp})])
        shell = DummyShell()
        command.make_ipython_v0_11_shell = lambda: None
        command.make_ipython_v0_10_shell = lambda: shell
        command.make_default_shell = lambda: None
        command.options.disable_ipython = False
        command.command()
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(shell.env, {
            'app':self.bootstrap.app, 'root':self.bootstrap.root,
            'registry':self.bootstrap.registry,
            'request':self.bootstrap.request,
            'root_factory':self.bootstrap.root_factory,
        })
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(shell.help)
    def test_command_get_root_hookable(self):
    def test_command_loads_custom_items(self):
        command = self._makeOne()
        interact = DummyInteractor()
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.interact = (interact,)
        command.loadapp = (loadapp,)
        root = Dummy()
        apps = []
        def get_root(app):
            apps.append(app)
            return root, lambda *arg: None
        command.get_root =get_root
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        class Options(object): pass
        command.options = Options()
        command.options.disable_ipython =True
        command.command(IPShell=None)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertTrue(loadapp.relative_to)
        self.assertEqual(len(app.threadlocal_manager.pushed), 0)
        self.assertEqual(interact.local, {'root':root,
                                          'registry':dummy_registry})
        self.assertTrue(interact.banner)
        self.assertEqual(apps, [app])
        model = Dummy()
        self.config_factory.items = [('m', model)]
        shell = DummyShell()
        command.command(shell)
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(shell.env, {
            'app':self.bootstrap.app, 'root':self.bootstrap.root,
            'registry':self.bootstrap.registry,
            'request':self.bootstrap.request,
            'root_factory':self.bootstrap.root_factory,
            'm':model,
        })
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(shell.help)
    def test_command_custom_section_override(self):
        command = self._makeOne()
        dummy = Dummy()
        self.config_factory.items = [('app', dummy), ('root', dummy),
                                     ('registry', dummy), ('request', dummy)]
        shell = DummyShell()
        command.command(shell)
        self.assertTrue(self.config_factory.parser)
        self.assertEqual(self.config_factory.parser.filename,
                         '/foo/bar/myapp.ini')
        self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp')
        self.assertEqual(shell.env, {
            'app':dummy, 'root':dummy, 'registry':dummy, 'request':dummy,
            'root_factory':self.bootstrap.root_factory,
        })
        self.assertTrue(self.bootstrap.closer.called)
        self.assertTrue(shell.help)
class TestPRoutesCommand(unittest.TestCase):
    def _getTargetClass(self):
@@ -141,7 +180,10 @@
        return PRoutesCommand
    def _makeOne(self):
        return self._getTargetClass()('proutes')
        cmd = self._getTargetClass()('proutes')
        cmd.bootstrap = (DummyBootstrap(),)
        cmd.args = ('/foo/bar/myapp.ini#myapp',)
        return cmd
    def test_no_routes(self):
        command = self._makeOne()
@@ -149,10 +191,6 @@
        command._get_mapper = lambda *arg: mapper
        L = []
        command.out = L.append
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L, [])
@@ -162,10 +200,6 @@
        command._get_mapper = lambda *arg:None
        L = []
        command.out = L.append
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L, [])
@@ -177,10 +211,6 @@
        command._get_mapper = lambda *arg: mapper
        L = []
        command.out = L.append
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(len(L), 3)
@@ -201,11 +231,7 @@
        command._get_mapper = lambda *arg: mapper
        L = []
        command.out = L.append
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.bootstrap = (DummyBootstrap(registry=registry),)
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(len(L), 3)
@@ -231,11 +257,7 @@
        command._get_mapper = lambda *arg: mapper
        L = []
        command.out = L.append
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.bootstrap = (DummyBootstrap(registry=registry),)
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(len(L), 3)
@@ -264,11 +286,7 @@
        command._get_mapper = lambda *arg: mapper
        L = []
        command.out = L.append
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp')
        command.bootstrap = (DummyBootstrap(registry=registry),)
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(len(L), 3)
@@ -279,10 +297,7 @@
        from pyramid.urldispatch import RoutesMapper
        command = self._makeOne()
        registry = Registry()
        class App: pass
        app = App()
        app.registry = registry
        result = command._get_mapper(app)
        result = command._get_mapper(registry)
        self.assertEqual(result.__class__, RoutesMapper)
        
class TestPViewsCommand(unittest.TestCase):
@@ -290,18 +305,22 @@
        from pyramid.paster import PViewsCommand
        return PViewsCommand
    def _makeOne(self):
        return self._getTargetClass()('pviews')
    def _makeOne(self, registry=None):
        cmd = self._getTargetClass()('pviews')
        cmd.bootstrap = (DummyBootstrap(registry=registry),)
        cmd.args = ('/foo/bar/myapp.ini#myapp',)
        return cmd
    def failUnless(self, condition):
        # silence stupid deprecation under Python >= 2.7
        self.assertTrue(condition)
    def _register_mapper(self, registry, routes):
        from pyramid.interfaces import IRoutesMapper
        mapper = DummyMapper(*routes)
        registry.registerUtility(mapper, IRoutesMapper)
    def test__find_view_no_match(self):
        from pyramid.registry import Registry
        registry = Registry()
        self._register_mapper(registry, [])
        command = self._makeOne()
        command = self._makeOne(registry)
        result = command._find_view('/a', registry)
        self.assertEqual(result, None)
@@ -323,7 +342,7 @@
                                 (IViewClassifier, IRequest, root_iface),
                                 IMultiView)
        self._register_mapper(registry, [])
        command = self._makeOne()
        command = self._makeOne(registry=registry)
        result = command._find_view('/x', registry)
        self.assertEqual(result, None)
@@ -343,7 +362,7 @@
                                 (IViewClassifier, IRequest, root_iface),
                                 IView, name='a')
        self._register_mapper(registry, [])
        command = self._makeOne()
        command = self._makeOne(registry=registry)
        result = command._find_view('/a', registry)
        self.assertEqual(result, view1)
@@ -366,7 +385,7 @@
                                 (IViewClassifier, IRequest, root_iface),
                                 IMultiView, name='a')
        self._register_mapper(registry, [])
        command = self._makeOne()
        command = self._makeOne(registry=registry)
        result = command._find_view('/a', registry)
        self.assertEqual(result, view)
@@ -394,7 +413,7 @@
        routes = [DummyRoute('a', '/a', factory=Factory, matchdict={}),
                  DummyRoute('b', '/b', factory=Factory)]
        self._register_mapper(registry, routes)
        command = self._makeOne()
        command = self._makeOne(registry=registry)
        result = command._find_view('/a', registry)
        self.assertEqual(result, view)
@@ -424,9 +443,9 @@
        routes = [DummyRoute('a', '/a', matchdict={}),
                  DummyRoute('b', '/a', matchdict={})]
        self._register_mapper(registry, routes)
        command = self._makeOne()
        command = self._makeOne(registry=registry)
        result = command._find_view('/a', registry)
        self.failUnless(IMultiView.providedBy(result))
        self.assertTrue(IMultiView.providedBy(result))
    def test__find_view_route_multiview(self):
        from zope.interface import Interface
@@ -462,12 +481,12 @@
        routes = [DummyRoute('a', '/a', matchdict={}),
                  DummyRoute('b', '/a', matchdict={})]
        self._register_mapper(registry, routes)
        command = self._makeOne()
        command = self._makeOne(registry=registry)
        result = command._find_view('/a', registry)
        self.failUnless(IMultiView.providedBy(result))
        self.assertTrue(IMultiView.providedBy(result))
        self.assertEqual(len(result.views), 2)
        self.failUnless((None, view1, None) in result.views)
        self.failUnless((None, view2, None) in result.views)
        self.assertTrue((None, view1, None) in result.views)
        self.assertTrue((None, view2, None) in result.views)
    def test__find_multi_routes_all_match(self):
        command = self._makeOne()
@@ -502,16 +521,12 @@
        
    def test_views_command_not_found(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        command._find_view = lambda arg1, arg2: None
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -519,16 +534,12 @@
    def test_views_command_not_found_url_starts_without_slash(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        command._find_view = lambda arg1, arg2: None
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', 'a')
        command.args = ('/foo/bar/myapp.ini#myapp', 'a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -536,17 +547,13 @@
    def test_views_command_single_view_traversal(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        view = DummyView(context='context', view_name='a')
        command._find_view = lambda arg1, arg2: view
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -556,18 +563,14 @@
    def test_views_command_single_view_function_traversal(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        def view(): pass
        view.__request_attrs__ = {'context': 'context', 'view_name': 'a'}
        command._find_view = lambda arg1, arg2: view
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -577,18 +580,14 @@
    def test_views_command_single_view_traversal_with_permission(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        view = DummyView(context='context', view_name='a')
        view.__permission__ = 'test'
        command._find_view = lambda arg1, arg2: view
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -599,8 +598,8 @@
    def test_views_command_single_view_traversal_with_predicates(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        def predicate(): pass
@@ -608,11 +607,7 @@
        view = DummyView(context='context', view_name='a')
        view.__predicates__ = [predicate]
        command._find_view = lambda arg1, arg2: view
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -623,19 +618,15 @@
    def test_views_command_single_view_route(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        route = DummyRoute('a', '/a', matchdict={})
        view = DummyView(context='context', view_name='a',
                         matched_route=route, subpath='')
        command._find_view = lambda arg1, arg2: view
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -650,8 +641,8 @@
    def test_views_command_multi_view_nested(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        view1 = DummyView(context='context', view_name='a1')
@@ -661,11 +652,7 @@
        multiview2 = DummyMultiView(multiview1, context='context',
                                    view_name='a')
        command._find_view = lambda arg1, arg2: multiview2
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -676,8 +663,8 @@
    def test_views_command_single_view_route_with_route_predicates(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        def predicate(): pass
@@ -686,11 +673,7 @@
        view = DummyView(context='context', view_name='a',
                         matched_route=route, subpath='')
        command._find_view = lambda arg1, arg2: view
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -706,8 +689,8 @@
    def test_views_command_multiview(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        view = DummyView(context='context')
@@ -715,11 +698,7 @@
        view.__view_attr__ = 'call'
        multiview = DummyMultiView(view, context='context', view_name='a')
        command._find_view = lambda arg1, arg2: multiview
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -729,8 +708,8 @@
    def test_views_command_multiview_with_permission(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        view = DummyView(context='context')
@@ -739,11 +718,7 @@
        view.__permission__ = 'test'
        multiview = DummyMultiView(view, context='context', view_name='a')
        command._find_view = lambda arg1, arg2: multiview
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -754,8 +729,8 @@
    def test_views_command_multiview_with_predicates(self):
        from pyramid.registry import Registry
        command = self._makeOne()
        registry = Registry()
        command = self._makeOne(registry=registry)
        L = []
        command.out = L.append
        def predicate(): pass
@@ -766,11 +741,7 @@
        view.__predicates__ = [predicate]
        multiview = DummyMultiView(view, context='context', view_name='a')
        command._find_view = lambda arg1, arg2: multiview
        app = DummyApp()
        app.registry = registry
        loadapp = DummyLoadApp(app)
        command.loadapp = (loadapp,)
        command.args = ('/foo/bar/myapp.ini', 'myapp', '/a')
        command.args = ('/foo/bar/myapp.ini#myapp', '/a')
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L[1], 'URL = /a')
@@ -778,11 +749,6 @@
        self.assertEqual(L[4], '    view name: a')
        self.assertEqual(L[8], '    pyramid.tests.test_paster.view.call')
        self.assertEqual(L[9], '    view predicates (predicate = x)')
    def _register_mapper(self, registry, routes):
        from pyramid.interfaces import IRoutesMapper
        mapper = DummyMapper(*routes)
        registry.registerUtility(mapper, IRoutesMapper)
class TestGetApp(unittest.TestCase):
    def _callFUT(self, config_file, section_name, loadapp):
@@ -798,41 +764,164 @@
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertEqual(loadapp.relative_to, os.getcwd())
        self.assertEqual(result, app)
    def test_it_with_hash(self):
        import os
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        result = self._callFUT('/foo/bar/myapp.ini#myapp', None, loadapp)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'myapp')
        self.assertEqual(loadapp.relative_to, os.getcwd())
        self.assertEqual(result, app)
    def test_it_with_hash_and_name_override(self):
        import os
        app = DummyApp()
        loadapp = DummyLoadApp(app)
        result = self._callFUT('/foo/bar/myapp.ini#myapp', 'yourapp', loadapp)
        self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
        self.assertEqual(loadapp.section_name, 'yourapp')
        self.assertEqual(loadapp.relative_to, os.getcwd())
        self.assertEqual(result, app)
class TestBootstrap(unittest.TestCase):
    def _callFUT(self, config_uri, request=None):
        from pyramid.paster import bootstrap
        return bootstrap(config_uri, request)
    def setUp(self):
        import pyramid.paster
        self.original_get_app = pyramid.paster.get_app
        self.original_prepare = pyramid.paster.prepare
        self.app = app = DummyApp()
        self.root = root = Dummy()
        class DummyGetApp(object):
            def __call__(self, *a, **kw):
                self.a = a
                self.kw = kw
                return app
        self.get_app = pyramid.paster.get_app = DummyGetApp()
        class DummyPrepare(object):
            def __call__(self, *a, **kw):
                self.a = a
                self.kw = kw
                return {'root':root, 'closer':lambda: None}
        self.getroot = pyramid.paster.prepare = DummyPrepare()
    def tearDown(self):
        import pyramid.paster
        pyramid.paster.get_app = self.original_get_app
        pyramid.paster.prepare = self.original_prepare
    def test_it_request_with_registry(self):
        request = DummyRequest({})
        request.registry = dummy_registry
        result = self._callFUT('/foo/bar/myapp.ini', request)
        self.assertEqual(result['app'], self.app)
        self.assertEqual(result['root'], self.root)
        self.assert_('closer' in result)
class TestPTweensCommand(unittest.TestCase):
    def _getTargetClass(self):
        from pyramid.paster import PTweensCommand
        return PTweensCommand
    def _makeOne(self):
        cmd = self._getTargetClass()('ptweens')
        cmd.bootstrap = (DummyBootstrap(),)
        cmd.args = ('/foo/bar/myapp.ini#myapp',)
        return cmd
    def test_command_no_tweens(self):
        command = self._makeOne()
        command._get_tweens = lambda *arg: None
        L = []
        command.out = L.append
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(L, [])
    def test_command_implicit_tweens_only(self):
        command = self._makeOne()
        tweens = DummyTweens([('name', 'item')], None)
        command._get_tweens = lambda *arg: tweens
        L = []
        command.out = L.append
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(
           L[0],
           '"pyramid.tweens" config value NOT set (implicitly ordered tweens '
            'used)')
    def test_command_implicit_and_explicit_tweens(self):
        command = self._makeOne()
        tweens = DummyTweens([('name', 'item')], [('name2', 'item2')])
        command._get_tweens = lambda *arg: tweens
        L = []
        command.out = L.append
        result = command.command()
        self.assertEqual(result, None)
        self.assertEqual(
           L[0],
           '"pyramid.tweens" config value set (explicitly ordered tweens used)')
    def test__get_tweens(self):
        command = self._makeOne()
        registry = DummyRegistry()
        self.assertEqual(command._get_tweens(registry), None)
class DummyTweens(object):
    def __init__(self, implicit, explicit):
        self._implicit = implicit
        self.explicit = explicit
        self.name_to_alias = {}
    def implicit(self):
        return self._implicit
class Dummy:
    pass
class DummyIPShellFactory(object):
    def __call__(self, argv, user_ns=None):
        shell = DummyIPShell()
        shell(user_ns, {})
        self.shell = shell
        return shell
class DummyIPShell(object):
    IP = Dummy()
    IP.BANNER = 'foo'
    def __call__(self, local_ns, global_ns):
        self.local_ns = local_ns
        self.global_ns = global_ns
    def mainloop(self):
        pass
dummy_root = Dummy()
class DummyRegistry(object):
    settings = {}
    def queryUtility(self, iface, default=None, name=''):
        return default
dummy_registry = DummyRegistry()
class DummyShell(object):
    env = {}
    help = ''
    def __call__(self, env, help):
        self.env = env
        self.help = help
class DummyInteractor:
    def __call__(self, banner, local):
        self.banner = banner
        self.local = local
class DummyIPShell(object):
    IP = Dummy()
    IP.BANNER = 'foo'
    def set_banner(self, banner):
        self.banner = banner
    def __call__(self):
        self.called = True
class DummyIPShellFactory(object):
    def __call__(self, **kw):
        self.kw = kw
        self.shell = DummyIPShell()
        return self.shell
class DummyLoadApp:
    def __init__(self, app):
@@ -847,22 +936,7 @@
class DummyApp:
    def __init__(self):
        self.registry = dummy_registry
        self.threadlocal_manager = DummyThreadLocalManager()
    def root_factory(self, environ):
        return dummy_root
class DummyThreadLocalManager:
    def __init__(self):
        self.pushed = []
        self.popped = []
    def push(self, item):
        self.pushed.append(item)
    def pop(self):
        self.popped.append(True)
class DummyMapper(object):
    def __init__(self, *routes):
        self.routes = routes
@@ -905,3 +979,59 @@
        self.views = [(None, view, None) for view in views]
        self.__request_attrs__ = attrs
class DummyConfigParser(object):
    def __init__(self, result):
        self.result = result
    def read(self, filename):
        self.filename = filename
    def items(self, section):
        self.section = section
        if self.result is None:
            from ConfigParser import NoSectionError
            raise NoSectionError, section
        return self.result
class DummyConfigParserFactory(object):
    items = None
    def __call__(self):
        self.parser = DummyConfigParser(self.items)
        return self.parser
class DummyCloser(object):
    def __call__(self):
        self.called = True
class DummyBootstrap(object):
    def __init__(self, app=None, registry=None, request=None, root=None,
                 root_factory=None, closer=None):
        self.app = app or DummyApp()
        if registry is None:
            registry = DummyRegistry()
        self.registry = registry
        if request is None:
            request = DummyRequest({})
        self.request = request
        if root is None:
            root = Dummy()
        self.root = root
        if root_factory is None:
            root_factory = Dummy()
        self.root_factory = root_factory
        if closer is None:
            closer = DummyCloser()
        self.closer = closer
    def __call__(self, *a, **kw):
        self.a = a
        self.kw = kw
        return {
            'app': self.app,
            'registry': self.registry,
            'request': self.request,
            'root': self.root,
            'root_factory': self.root_factory,
            'closer': self.closer,
        }
pyramid/tests/test_renderers.py
@@ -341,11 +341,15 @@
class TestRendererFromName(unittest.TestCase):
    def setUp(self):
        from zope.deprecation import __show__
        __show__.off()
        self.config = cleanUp()
    def tearDown(self):
        cleanUp()
        from zope.deprecation import __show__
        __show__.on()
    def _callFUT(self, path, package=None):
        from pyramid.renderers import renderer_from_name
        return renderer_from_name(path, package)
@@ -481,6 +485,13 @@
            settings = None
        helper = self._makeOne(registry=Dummy)
        self.assertEqual(helper.settings, {})
    def test_settings_registry_name_is_None(self):
        class Dummy(object):
            settings = None
        helper = self._makeOne(registry=Dummy)
        self.assertEqual(helper.name, None)
        self.assertEqual(helper.type, '')
    def test_settings_registry_settings_is_not_None(self):
        class Dummy(object):
@@ -687,6 +698,56 @@
        self.assertEqual(response.status, '406 You Lose')
        self.assertEqual(response.body, 'abc')
    def test_clone_noargs(self):
        helper = self._makeOne('name', 'package', 'registry')
        cloned_helper = helper.clone()
        self.assertEqual(cloned_helper.name, 'name')
        self.assertEqual(cloned_helper.package, 'package')
        self.assertEqual(cloned_helper.registry, 'registry')
        self.assertFalse(helper is cloned_helper)
    def test_clone_allargs(self):
        helper = self._makeOne('name', 'package', 'registry')
        cloned_helper = helper.clone(name='name2', package='package2',
                                     registry='registry2')
        self.assertEqual(cloned_helper.name, 'name2')
        self.assertEqual(cloned_helper.package, 'package2')
        self.assertEqual(cloned_helper.registry, 'registry2')
        self.assertFalse(helper is cloned_helper)
class TestNullRendererHelper(unittest.TestCase):
    def setUp(self):
        self.config = cleanUp()
    def tearDown(self):
        cleanUp()
    def _makeOne(self, *arg, **kw):
        from pyramid.renderers import NullRendererHelper
        return NullRendererHelper(*arg, **kw)
    def test_instance_conforms(self):
        from zope.interface.verify import verifyObject
        from pyramid.interfaces import IRendererInfo
        helper = self._makeOne()
        verifyObject(IRendererInfo, helper)
    def test_render_view(self):
        helper = self._makeOne()
        self.assertEqual(helper.render_view(None, True, None, None), True)
    def test_render(self):
        helper = self._makeOne()
        self.assertEqual(helper.render(True, None, None), True)
    def test_render_to_response(self):
        helper = self._makeOne()
        self.assertEqual(helper.render_to_response(True, None, None), True)
    def test_clone(self):
        helper = self._makeOne()
        self.assertTrue(helper.clone() is helper)
class Test_render(unittest.TestCase):
    def setUp(self):
        self.config = testing.setUp()
pyramid/tests/test_request.py
@@ -233,25 +233,28 @@
        request.registry.registerAdapter(adapter, (Foo,), IResponse)
        self.assertEqual(request.is_response(foo), True)
    def test_json_incorrect_mimetype(self):
        request = self._makeOne({})
        self.assertEqual(request.json, None)
    def test_json_body_invalid_json(self):
        request = self._makeOne({'REQUEST_METHOD':'POST'})
        request.body = '{'
        self.assertRaises(ValueError, getattr, request, 'json_body')
        
    def test_json_correct_mimetype(self):
        request = self._makeOne({})
        request.content_type = 'application/json'
    def test_json_body_valid_json(self):
        request = self._makeOne({'REQUEST_METHOD':'POST'})
        request.body = '{"a":1}'
        self.assertEqual(request.json, {'a':1})
        self.assertEqual(request.json_body, {'a':1})
    def test_json_alternate_charset(self):
    def test_json_body_alternate_charset(self):
        from pyramid.compat import json
        request = self._makeOne({})
        request.content_type = 'application/json'
        request = self._makeOne({'REQUEST_METHOD':'POST'})
        request.charset = 'latin-1'
        la = unicode('La Pe\xc3\xb1a', 'utf-8')
        body = json.dumps({'a':la}, encoding='latin-1')
        request.body = body
        self.assertEqual(request.json, {'a':la})
        self.assertEqual(request.json_body, {'a':la})
    def test_json_body_GET_request(self):
        request = self._makeOne({'REQUEST_METHOD':'GET'})
        self.assertRaises(ValueError, getattr, request, 'json_body')
class TestRequestDeprecatedMethods(unittest.TestCase):
    def setUp(self):
@@ -421,6 +424,15 @@
        self.assertTrue(hasattr(iface, 'combined'))
        self.assertEqual(iface.combined.__name__, 'routename_combined_IRequest')
    def test_it_routename_with_spaces(self):
        #  see https://github.com/Pylons/pyramid/issues/232
        iface = self._callFUT('routename with spaces')
        self.assertEqual(iface.__name__, 'routename with spaces_IRequest')
        self.assertTrue(hasattr(iface, 'combined'))
        self.assertEqual(iface.combined.__name__,
                         'routename with spaces_combined_IRequest')
class Test_add_global_response_headers(unittest.TestCase):
    def _callFUT(self, request, headerlist):
        from pyramid.request import add_global_response_headers
pyramid/tests/test_response.py
@@ -1,4 +1,5 @@
import unittest
from pyramid import testing
class TestResponse(unittest.TestCase):
    def _getTargetClass(self):
@@ -15,3 +16,66 @@
        inst = self._getTargetClass()()
        self.assertTrue(IResponse.providedBy(inst))
class Dummy(object):
    pass
class DummyConfigurator(object):
    def __init__(self):
        self.adapters = []
    def add_response_adapter(self, wrapped, type_or_iface):
        self.adapters.append((wrapped, type_or_iface))
class DummyVenusian(object):
    def __init__(self):
        self.attached = []
    def attach(self, wrapped, fn, category=None):
        self.attached.append((wrapped, fn, category))
class TestResponseAdapter(unittest.TestCase):
    def setUp(self):
        registry = Dummy()
        self.config = testing.setUp(registry=registry)
        self.config.begin()
    def tearDown(self):
        self.config.end()
    def _makeOne(self, *types_or_ifaces):
        from pyramid.response import response_adapter
        return response_adapter(*types_or_ifaces)
    def test_register_single(self):
        from zope.interface import Interface
        class IFoo(Interface): pass
        dec = self._makeOne(IFoo)
        def foo(): pass
        config = DummyConfigurator()
        scanner = Dummy()
        scanner.config = config
        dec.register(scanner, None, foo)
        self.assertEqual(config.adapters, [(foo, IFoo)])
    def test_register_multi(self):
        from zope.interface import Interface
        class IFoo(Interface): pass
        class IBar(Interface): pass
        dec = self._makeOne(IFoo, IBar)
        def foo(): pass
        config = DummyConfigurator()
        scanner = Dummy()
        scanner.config = config
        dec.register(scanner, None, foo)
        self.assertEqual(config.adapters, [(foo, IFoo), (foo, IBar)])
    def test___call__(self):
        from zope.interface import Interface
        class IFoo(Interface): pass
        dec = self._makeOne(IFoo)
        dummy_venusian = DummyVenusian()
        dec.venusian = dummy_venusian
        def foo(): pass
        dec(foo)
        self.assertEqual(dummy_venusian.attached,
                         [(foo, dec.register, 'pyramid')])
pyramid/tests/test_router.py
@@ -4,9 +4,8 @@
class TestRouter(unittest.TestCase):
    def setUp(self):
        testing.setUp()
        from pyramid.threadlocal import get_current_registry
        self.registry = get_current_registry()
        self.config = testing.setUp()
        self.registry = self.config.registry
    def tearDown(self):
        testing.tearDown()
@@ -135,6 +134,53 @@
        router = self._makeOne()
        self.assertEqual(router.request_factory, DummyRequestFactory)
    def test_tween_factories(self):
        from pyramid.interfaces import ITweens
        from pyramid.config import Tweens
        from pyramid.response import Response
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IResponse
        tweens = Tweens()
        self.registry.registerUtility(tweens, ITweens)
        L = []
        def tween_factory1(handler, registry):
            L.append((handler, registry))
            def wrapper(request):
                request.environ['handled'].append('one')
                return handler(request)
            wrapper.name = 'one'
            wrapper.child = handler
            return wrapper
        def tween_factory2(handler, registry):
            L.append((handler, registry))
            def wrapper(request):
                request.environ['handled'] = ['two']
                return handler(request)
            wrapper.name = 'two'
            wrapper.child = handler
            return wrapper
        tweens.add_implicit('one', tween_factory1)
        tweens.add_implicit('two', tween_factory2)
        router = self._makeOne()
        self.assertEqual(router.handle_request.name, 'two')
        self.assertEqual(router.handle_request.child.name, 'one')
        self.assertEqual(router.handle_request.child.child.__name__,
                         'handle_request')
        context = DummyContext()
        self._registerTraverserFactory(context)
        environ = self._makeEnviron()
        view = DummyView('abc')
        self._registerView(self.config.derive_view(view), '',
                           IViewClassifier, None, None)
        start_response = DummyStartResponse()
        def make_response(s):
            return Response(s)
        router.registry.registerAdapter(make_response, (str,), IResponse)
        app_iter = router(environ, start_response)
        self.assertEqual(app_iter, ['abc'])
        self.assertEqual(start_response.status, '200 OK')
        self.assertEqual(environ['handled'], ['two', 'one'])
    def test_call_traverser_default(self):
        from pyramid.httpexceptions import HTTPNotFound
        environ = self._makeEnviron()
@@ -242,7 +288,8 @@
        self._registerTraverserFactory(context)
        environ = self._makeEnviron()
        view = DummyView('abc')
        self._registerView(view, '', IViewClassifier, None, None)
        self._registerView(self.config.derive_view(view), '', IViewClassifier,
                           None, None)
        router = self._makeOne()
        start_response = DummyStartResponse()
        self.assertRaises(ValueError, router, environ, start_response)
@@ -255,7 +302,8 @@
        self._registerTraverserFactory(context)
        environ = self._makeEnviron()
        view = DummyView('abc')
        self._registerView(view, '', IViewClassifier, None, None)
        self._registerView(self.config.derive_view(view), '',
                           IViewClassifier, None, None)
        router = self._makeOne()
        start_response = DummyStartResponse()
        def make_response(s):
@@ -273,7 +321,8 @@
        response.app_iter = ['Hello world']
        view = DummyView(response)
        environ = self._makeEnviron()
        self._registerView(view, '', IViewClassifier, None, None)
        self._registerView(self.config.derive_view(view), '',
                           IViewClassifier, None, None)
        self._registerRootFactory(context)
        router = self._makeOne()
        start_response = DummyStartResponse()
@@ -400,6 +449,33 @@
        start_response = DummyStartResponse()
        why = exc_raised(HTTPNotFound, router, environ, start_response)
        self.assertEqual(why[0], 'notfound')
    def test_call_view_raises_response_cleared(self):
        from zope.interface import Interface
        from zope.interface import directlyProvides
        from pyramid.interfaces import IExceptionViewClassifier
        class IContext(Interface):
            pass
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IViewClassifier
        context = DummyContext()
        directlyProvides(context, IContext)
        self._registerTraverserFactory(context, subpath=[''])
        def view(context, request):
            request.response.a = 1
            raise KeyError
        def exc_view(context, request):
            self.assertFalse(hasattr(request.response, 'a'))
            request.response.body = 'OK'
            return request.response
        environ = self._makeEnviron()
        self._registerView(view, '', IViewClassifier, IRequest, IContext)
        self._registerView(exc_view, '', IExceptionViewClassifier,
                           IRequest, KeyError)
        router = self._makeOne()
        start_response = DummyStartResponse()
        itera = router(environ, start_response)
        self.assertEqual(itera, ['OK'])
    def test_call_request_has_response_callbacks(self):
        from zope.interface import Interface
@@ -713,6 +789,7 @@
        from pyramid.interfaces import IRequest
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IRequestFactory
        from pyramid.interfaces import IExceptionViewClassifier
        def rfactory(environ):
            return request
        self.registry.registerUtility(rfactory, IRequestFactory)
@@ -722,16 +799,23 @@
        directlyProvides(context, IContext)
        self._registerTraverserFactory(context, subpath=[''])
        response = DummyResponse()
        response.app_iter = ['OK']
        view = DummyView(response, raise_exception=RuntimeError)
        environ = self._makeEnviron()
        def exception_view(context, request):
            self.assertEqual(request.exc_info[0], RuntimeError)
            return response
        self._registerView(view, '', IViewClassifier, IRequest, IContext)
        self._registerView(exception_view, '', IExceptionViewClassifier,
                           IRequest, RuntimeError)
        router = self._makeOne()
        start_response = DummyStartResponse()
        self.assertRaises(RuntimeError, router, environ, start_response)
        # ``exception`` must be attached to request even if a suitable
        # exception view cannot be found
        self.assertEqual(request.exception.__class__, RuntimeError)
        result = router(environ, start_response)
        self.assertEqual(result, ['OK'])
        # we clean up the exc_info and exception after the request
        self.assertEqual(request.exception, None)
        self.assertEqual(request.exc_info, None)
    def test_call_view_raises_exception_view(self):
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IExceptionViewClassifier
@@ -740,7 +824,9 @@
        exception_response = DummyResponse()
        exception_response.app_iter = ["Hello, world"]
        view = DummyView(response, raise_exception=RuntimeError)
        exception_view = DummyView(exception_response)
        def exception_view(context, request):
            self.assertEqual(request.exception.__class__, RuntimeError)
            return exception_response
        environ = self._makeEnviron()
        self._registerView(view, '', IViewClassifier, IRequest, None)
        self._registerView(exception_view, '', IExceptionViewClassifier,
@@ -749,7 +835,6 @@
        start_response = DummyStartResponse()
        result = router(environ, start_response)
        self.assertEqual(result, ["Hello, world"])
        self.assertEqual(view.request.exception.__class__, RuntimeError)
    def test_call_view_raises_super_exception_sub_exception_view(self):
        from pyramid.interfaces import IViewClassifier
@@ -856,9 +941,12 @@
        environ = self._makeEnviron()
        response = DummyResponse()
        view = DummyView(response, raise_exception=RuntimeError)
        self._registerView(view, '', IViewClassifier, IRequest, None)
        self._registerView(self.config.derive_view(view), '',
                           IViewClassifier, IRequest, None)
        exception_view = DummyView(None)
        self._registerView(exception_view, '', IExceptionViewClassifier,
        self._registerView(self.config.derive_view(exception_view), '',
                           IExceptionViewClassifier,
                           IRequest, RuntimeError)
        router = self._makeOne()
        start_response = DummyStartResponse()
pyramid/tests/test_scripting.py
@@ -1,12 +1,12 @@
import unittest
class TestGetRoot(unittest.TestCase):
class Test_get_root(unittest.TestCase):
    def _callFUT(self, app, request=None):
        from pyramid.paster import get_root
        from pyramid.scripting import get_root
        return get_root(app, request)
    def test_it_norequest(self):
        app = DummyApp()
        app = DummyApp(registry=dummy_registry)
        root, closer = self._callFUT(app)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
@@ -17,7 +17,7 @@
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
    def test_it_withrequest(self):
        app = DummyApp()
        app = DummyApp(registry=dummy_registry)
        request = DummyRequest({})
        root, closer = self._callFUT(app, request)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
@@ -29,37 +29,115 @@
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
    def test_it_requestfactory_overridden(self):
        app = DummyApp()
        request = Dummy()
        class DummyFactory(object):
            @classmethod
            def blank(cls, path):
                return request
        registry = DummyRegistry(DummyFactory)
        app.registry = registry
        app = DummyApp(registry=dummy_registry)
        root, closer = self._callFUT(app)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['request'].environ['path'], '/')
class Test_prepare(unittest.TestCase):
    def _callFUT(self, request=None, registry=None):
        from pyramid.scripting import prepare
        return prepare(request, registry)
    def _makeRegistry(self):
        return DummyRegistry(DummyFactory)
    def setUp(self):
        from pyramid.threadlocal import manager
        self.manager = manager
        self.default = manager.get()
    def test_it_no_valid_apps(self):
        from pyramid.exceptions import ConfigurationError
        self.assertRaises(ConfigurationError, self._callFUT)
    def test_it_norequest(self):
        registry = self._makeRegistry()
        info = self._callFUT(registry=registry)
        root, closer = info['root'], info['closer']
        pushed = self.manager.get()
        self.assertEqual(pushed['registry'], registry)
        self.assertEqual(pushed['request'].registry, registry)
        self.assertEqual(root.a, (pushed['request'],))
        closer()
        self.assertEqual(self.default, self.manager.get())
    def test_it_withrequest(self):
        request = DummyRequest({})
        registry = request.registry = self._makeRegistry()
        info = self._callFUT(request=request)
        root, closer = info['root'], info['closer']
        pushed = self.manager.get()
        self.assertEqual(pushed['request'], request)
        self.assertEqual(pushed['registry'], registry)
        self.assertEqual(pushed['request'].registry, registry)
        self.assertEqual(root.a, (request,))
        closer()
        self.assertEqual(self.default, self.manager.get())
    def test_it_with_request_and_registry(self):
        request = DummyRequest({})
        registry = request.registry = self._makeRegistry()
        info = self._callFUT(request=request, registry=registry)
        root, closer = info['root'], info['closer']
        pushed = self.manager.get()
        self.assertEqual(pushed['request'], request)
        self.assertEqual(pushed['registry'], registry)
        self.assertEqual(pushed['request'].registry, registry)
        self.assertEqual(root.a, (request,))
        closer()
        self.assertEqual(self.default, self.manager.get())
class Test__make_request(unittest.TestCase):
    def _callFUT(self, path='/', registry=None):
        from pyramid.scripting import _make_request
        return _make_request(path, registry)
    def test_it_with_registry(self):
        request = self._callFUT('/', dummy_registry)
        self.assertEqual(request.environ['path'], '/')
        self.assertEqual(request.registry, dummy_registry)
    def test_it_with_no_registry(self):
        from pyramid.config import global_registries
        # keep registry local so that global_registries is cleared after
        registry = DummyRegistry(DummyFactory)
        global_registries.add(registry)
        request = self._callFUT('/hello')
        self.assertEqual(request.environ['path'], '/hello')
        self.assertEqual(request.registry, registry)
        global_registries.empty()
class Dummy:
    pass
dummy_root = Dummy()
class DummyFactory(object):
    @classmethod
    def blank(cls, path):
        req = DummyRequest({'path': path})
        return req
    def __init__(self, *a, **kw):
        self.a = a
        self.kw = kw
class DummyRegistry(object):
    def __init__(self, result=None):
        self.result = result
    def __init__(self, factory=None):
        self.factory = factory
    def queryUtility(self, iface, default=None):
        return self.result or default
        return self.factory or default
dummy_registry = DummyRegistry()
dummy_registry = DummyRegistry(DummyFactory)
class DummyApp:
    def __init__(self):
        self.registry = dummy_registry
    def __init__(self, registry=None):
        self.threadlocal_manager = DummyThreadLocalManager()
        if registry:
            self.registry = registry
    def root_factory(self, environ):
        return dummy_root
pyramid/tests/test_settings.py
@@ -28,152 +28,337 @@
        self.assertEqual(settings['reload_templates'], False)
        self.assertEqual(settings['reload_resources'], False)
        self.assertEqual(settings['pyramid.debug_authorization'], False)
        self.assertEqual(settings['pyramid.debug_notfound'], False)
        self.assertEqual(settings['pyramid.debug_routematch'], False)
        self.assertEqual(settings['pyramid.reload_templates'], False)
        self.assertEqual(settings['pyramid.reload_resources'], False)
    def test_prevent_http_cache(self):
        settings = self._makeOne({})
        self.assertEqual(settings['prevent_http_cache'], False)
        self.assertEqual(settings['pyramid.prevent_http_cache'], False)
        result = self._makeOne({'prevent_http_cache':'false'})
        self.assertEqual(result['prevent_http_cache'], False)
        self.assertEqual(result['pyramid.prevent_http_cache'], False)
        result = self._makeOne({'prevent_http_cache':'t'})
        self.assertEqual(result['prevent_http_cache'], True)
        self.assertEqual(result['pyramid.prevent_http_cache'], True)
        result = self._makeOne({'prevent_http_cache':'1'})
        self.assertEqual(result['prevent_http_cache'], True)
        self.assertEqual(result['pyramid.prevent_http_cache'], True)
        result = self._makeOne({'pyramid.prevent_http_cache':'t'})
        self.assertEqual(result['prevent_http_cache'], True)
        self.assertEqual(result['pyramid.prevent_http_cache'], True)
        result = self._makeOne({}, {'PYRAMID_PREVENT_HTTP_CACHE':'1'})
        self.assertEqual(result['prevent_http_cache'], True)
        self.assertEqual(result['pyramid.prevent_http_cache'], True)
        result = self._makeOne({'prevent_http_cache':'false',
                                'pyramid.prevent_http_cache':'1'})
        self.assertEqual(result['prevent_http_cache'], True)
        self.assertEqual(result['pyramid.prevent_http_cache'], True)
        result = self._makeOne({'prevent_http_cache':'false',
                                'pyramid.prevent_http_cache':'f'},
                               {'PYRAMID_PREVENT_HTTP_CACHE':'1'})
        self.assertEqual(result['prevent_http_cache'], True)
        self.assertEqual(result['pyramid.prevent_http_cache'], True)
    def test_reload_templates(self):
        settings = self._makeOne({})
        self.assertEqual(settings['reload_templates'], False)
        self.assertEqual(settings['pyramid.reload_templates'], False)
        result = self._makeOne({'reload_templates':'false'})
        self.assertEqual(result['reload_templates'], False)
        self.assertEqual(result['pyramid.reload_templates'], False)
        result = self._makeOne({'reload_templates':'t'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['pyramid.reload_templates'], True)
        result = self._makeOne({'reload_templates':'1'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['pyramid.reload_templates'], True)
        result = self._makeOne({'pyramid.reload_templates':'1'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['pyramid.reload_templates'], True)
        result = self._makeOne({}, {'PYRAMID_RELOAD_TEMPLATES':'1'})
        self.assertEqual(result['reload_templates'], True)
        result = self._makeOne({'reload_templates':'false'},
                             {'PYRAMID_RELOAD_TEMPLATES':'1'})
        self.assertEqual(result['pyramid.reload_templates'], True)
        result = self._makeOne({'reload_templates':'false',
                                'pyramid.reload_templates':'1'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['pyramid.reload_templates'], True)
        result = self._makeOne({'reload_templates':'false'},
                               {'PYRAMID_RELOAD_TEMPLATES':'1'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['pyramid.reload_templates'], True)
    def test_reload_resources(self):
        # alias for reload_assets
        result = self._makeOne({})
        self.assertEqual(result['reload_resources'], False)
        self.assertEqual(result['reload_assets'], False)
        self.assertEqual(result['pyramid.reload_resources'], False)
        self.assertEqual(result['pyramid.reload_assets'], False)
        result = self._makeOne({'reload_resources':'false'})
        self.assertEqual(result['reload_resources'], False)
        self.assertEqual(result['reload_assets'], False)
        self.assertEqual(result['pyramid.reload_resources'], False)
        self.assertEqual(result['pyramid.reload_assets'], False)
        result = self._makeOne({'reload_resources':'t'})
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        result = self._makeOne({'reload_resources':'1'})
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        result = self._makeOne({'pyramid.reload_resources':'1'})
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        result = self._makeOne({}, {'PYRAMID_RELOAD_RESOURCES':'1'})
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        result = self._makeOne({'reload_resources':'false'},
                             {'PYRAMID_RELOAD_RESOURCES':'1'})
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        result = self._makeOne({'reload_resources':'false',
                                'pyramid.reload_resources':'1'})
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        result = self._makeOne({'reload_resources':'false',
                                'pyramid.reload_resources':'false'},
                               {'PYRAMID_RELOAD_RESOURCES':'1'})
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
    def test_reload_assets(self):
        # alias for reload_resources
        result = self._makeOne({})
        self.assertEqual(result['reload_assets'], False)
        self.assertEqual(result['reload_resources'], False)
        self.assertEqual(result['pyramid.reload_assets'], False)
        self.assertEqual(result['pyramid.reload_resources'], False)
        result = self._makeOne({'reload_assets':'false'})
        self.assertEqual(result['reload_resources'], False)
        self.assertEqual(result['reload_assets'], False)
        self.assertEqual(result['pyramid.reload_assets'], False)
        self.assertEqual(result['pyramid.reload_resources'], False)
        result = self._makeOne({'reload_assets':'t'})
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        result = self._makeOne({'reload_assets':'1'})
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        result = self._makeOne({'pyramid.reload_assets':'1'})
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        result = self._makeOne({}, {'PYRAMID_RELOAD_ASSETS':'1'})
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['reload_resources'], True)
        result = self._makeOne({'reload_assets':'false'},
                             {'PYRAMID_RELOAD_ASSETS':'1'})
        self.assertEqual(result['pyramid.reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        result = self._makeOne({'reload_assets':'false',
                                'pyramid.reload_assets':'1'})
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        result = self._makeOne({'reload_assets':'false',
                                'pyramid.reload_assets':'false'},
                               {'PYRAMID_RELOAD_ASSETS':'1'})
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
    def test_reload_all(self):
        result = self._makeOne({})
        self.assertEqual(result['reload_templates'], False)
        self.assertEqual(result['reload_resources'], False)
        self.assertEqual(result['reload_assets'], False)
        self.assertEqual(result['pyramid.reload_templates'], False)
        self.assertEqual(result['pyramid.reload_resources'], False)
        self.assertEqual(result['pyramid.reload_assets'], False)
        result = self._makeOne({'reload_all':'false'})
        self.assertEqual(result['reload_templates'], False)
        self.assertEqual(result['reload_resources'], False)
        self.assertEqual(result['reload_assets'], False)
        self.assertEqual(result['pyramid.reload_templates'], False)
        self.assertEqual(result['pyramid.reload_resources'], False)
        self.assertEqual(result['pyramid.reload_assets'], False)
        result = self._makeOne({'reload_all':'t'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['pyramid.reload_templates'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        result = self._makeOne({'reload_all':'1'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['pyramid.reload_templates'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        result = self._makeOne({'pyramid.reload_all':'1'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['pyramid.reload_templates'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        result = self._makeOne({}, {'PYRAMID_RELOAD_ALL':'1'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        result = self._makeOne({'reload_all':'false'},
                             {'PYRAMID_RELOAD_ALL':'1'})
        self.assertEqual(result['pyramid.reload_templates'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        result = self._makeOne({'reload_all':'false',
                                'pyramid.reload_all':'1'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['pyramid.reload_templates'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
        result = self._makeOne({'reload_all':'false',
                                'pyramid.reload_all':'false'},
                               {'PYRAMID_RELOAD_ALL':'1'})
        self.assertEqual(result['reload_templates'], True)
        self.assertEqual(result['reload_resources'], True)
        self.assertEqual(result['reload_assets'], True)
        self.assertEqual(result['pyramid.reload_templates'], True)
        self.assertEqual(result['pyramid.reload_resources'], True)
        self.assertEqual(result['pyramid.reload_assets'], True)
    def test_debug_authorization(self):
        result = self._makeOne({})
        self.assertEqual(result['debug_authorization'], False)
        self.assertEqual(result['pyramid.debug_authorization'], False)
        result = self._makeOne({'debug_authorization':'false'})
        self.assertEqual(result['debug_authorization'], False)
        self.assertEqual(result['pyramid.debug_authorization'], False)
        result = self._makeOne({'debug_authorization':'t'})
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
        result = self._makeOne({'debug_authorization':'1'})
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
        result = self._makeOne({'pyramid.debug_authorization':'1'})
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
        result = self._makeOne({}, {'PYRAMID_DEBUG_AUTHORIZATION':'1'})
        self.assertEqual(result['debug_authorization'], True)
        result = self._makeOne({'debug_authorization':'false'},
                             {'PYRAMID_DEBUG_AUTHORIZATION':'1'})
        self.assertEqual(result['pyramid.debug_authorization'], True)
        result = self._makeOne({'debug_authorization':'false',
                                'pyramid.debug_authorization':'1'})
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
        result = self._makeOne({'debug_authorization':'false',
                                'pyramid.debug_authorization':'false'},
                               {'PYRAMID_DEBUG_AUTHORIZATION':'1'})
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
    def test_debug_notfound(self):
        result = self._makeOne({})
        self.assertEqual(result['debug_notfound'], False)
        self.assertEqual(result['pyramid.debug_notfound'], False)
        result = self._makeOne({'debug_notfound':'false'})
        self.assertEqual(result['debug_notfound'], False)
        self.assertEqual(result['pyramid.debug_notfound'], False)
        result = self._makeOne({'debug_notfound':'t'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_notfound'], True)
        result = self._makeOne({'debug_notfound':'1'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_notfound'], True)
        result = self._makeOne({'pyramid.debug_notfound':'1'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_notfound'], True)
        result = self._makeOne({}, {'PYRAMID_DEBUG_NOTFOUND':'1'})
        self.assertEqual(result['debug_notfound'], True)
        result = self._makeOne({'debug_notfound':'false'},
                             {'PYRAMID_DEBUG_NOTFOUND':'1'})
        self.assertEqual(result['pyramid.debug_notfound'], True)
        result = self._makeOne({'debug_notfound':'false',
                                'pyramid.debug_notfound':'1'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_notfound'], True)
        result = self._makeOne({'debug_notfound':'false',
                                'pyramid.debug_notfound':'false'},
                               {'PYRAMID_DEBUG_NOTFOUND':'1'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_notfound'], True)
    def test_debug_routematch(self):
        result = self._makeOne({})
        self.assertEqual(result['debug_routematch'], False)
        self.assertEqual(result['pyramid.debug_routematch'], False)
        result = self._makeOne({'debug_routematch':'false'})
        self.assertEqual(result['debug_routematch'], False)
        self.assertEqual(result['pyramid.debug_routematch'], False)
        result = self._makeOne({'debug_routematch':'t'})
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
        result = self._makeOne({'debug_routematch':'1'})
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
        result = self._makeOne({'pyramid.debug_routematch':'1'})
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
        result = self._makeOne({}, {'PYRAMID_DEBUG_ROUTEMATCH':'1'})
        self.assertEqual(result['debug_routematch'], True)
        result = self._makeOne({'debug_routematch':'false'},
                             {'PYRAMID_DEBUG_ROUTEMATCH':'1'})
        self.assertEqual(result['pyramid.debug_routematch'], True)
        result = self._makeOne({'debug_routematch':'false',
                                'pyramid.debug_routematch':'1'})
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
        result = self._makeOne({'debug_routematch':'false',
                                'pyramid.debug_routematch':'false'},
                               {'PYRAMID_DEBUG_ROUTEMATCH':'1'})
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
    def test_debug_templates(self):
        result = self._makeOne({})
        self.assertEqual(result['debug_templates'], False)
        self.assertEqual(result['pyramid.debug_templates'], False)
        result = self._makeOne({'debug_templates':'false'})
        self.assertEqual(result['debug_templates'], False)
        self.assertEqual(result['pyramid.debug_templates'], False)
        result = self._makeOne({'debug_templates':'t'})
        self.assertEqual(result['debug_templates'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
        result = self._makeOne({'debug_templates':'1'})
        self.assertEqual(result['debug_templates'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
        result = self._makeOne({'pyramid.debug_templates':'1'})
        self.assertEqual(result['debug_templates'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
        result = self._makeOne({}, {'PYRAMID_DEBUG_TEMPLATES':'1'})
        self.assertEqual(result['debug_templates'], True)
        result = self._makeOne({'debug_templates':'false'},
                             {'PYRAMID_DEBUG_TEMPLATES':'1'})
        self.assertEqual(result['pyramid.debug_templates'], True)
        result = self._makeOne({'debug_templates':'false',
                                'pyramid.debug_templates':'1'})
        self.assertEqual(result['debug_templates'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
        result = self._makeOne({'debug_templates':'false',
                                'pyramid.debug_templates':'false'},
                               {'PYRAMID_DEBUG_TEMPLATES':'1'})
        self.assertEqual(result['debug_templates'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
    def test_debug_all(self):
        result = self._makeOne({})
@@ -181,43 +366,99 @@
        self.assertEqual(result['debug_routematch'], False)
        self.assertEqual(result['debug_authorization'], False)
        self.assertEqual(result['debug_templates'], False)
        self.assertEqual(result['pyramid.debug_notfound'], False)
        self.assertEqual(result['pyramid.debug_routematch'], False)
        self.assertEqual(result['pyramid.debug_authorization'], False)
        self.assertEqual(result['pyramid.debug_templates'], False)
        result = self._makeOne({'debug_all':'false'})
        self.assertEqual(result['debug_notfound'], False)
        self.assertEqual(result['debug_routematch'], False)
        self.assertEqual(result['debug_authorization'], False)
        self.assertEqual(result['debug_templates'], False)
        self.assertEqual(result['pyramid.debug_notfound'], False)
        self.assertEqual(result['pyramid.debug_routematch'], False)
        self.assertEqual(result['pyramid.debug_authorization'], False)
        self.assertEqual(result['pyramid.debug_templates'], False)
        result = self._makeOne({'debug_all':'t'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['debug_templates'], True)
        self.assertEqual(result['pyramid.debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
        result = self._makeOne({'debug_all':'1'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['debug_templates'], True)
        self.assertEqual(result['pyramid.debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
        result = self._makeOne({'pyramid.debug_all':'1'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['debug_templates'], True)
        self.assertEqual(result['pyramid.debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
        result = self._makeOne({}, {'PYRAMID_DEBUG_ALL':'1'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['debug_templates'], True)
        result = self._makeOne({'debug_all':'false'},
                             {'PYRAMID_DEBUG_ALL':'1'})
        self.assertEqual(result['pyramid.debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
        result = self._makeOne({'debug_all':'false',
                                'pyramid.debug_all':'1'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['debug_templates'], True)
        self.assertEqual(result['pyramid.debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
        result = self._makeOne({'debug_all':'false',
                                'pyramid.debug_all':'false'},
                               {'PYRAMID_DEBUG_ALL':'1'})
        self.assertEqual(result['debug_notfound'], True)
        self.assertEqual(result['debug_routematch'], True)
        self.assertEqual(result['debug_authorization'], True)
        self.assertEqual(result['debug_templates'], True)
        self.assertEqual(result['pyramid.debug_notfound'], True)
        self.assertEqual(result['pyramid.debug_routematch'], True)
        self.assertEqual(result['pyramid.debug_authorization'], True)
        self.assertEqual(result['pyramid.debug_templates'], True)
    def test_default_locale_name(self):
        result = self._makeOne({})
        self.assertEqual(result['default_locale_name'], 'en')
        self.assertEqual(result['pyramid.default_locale_name'], 'en')
        result = self._makeOne({'default_locale_name':'abc'})
        self.assertEqual(result['default_locale_name'], 'abc')
        self.assertEqual(result['pyramid.default_locale_name'], 'abc')
        result = self._makeOne({'pyramid.default_locale_name':'abc'})
        self.assertEqual(result['default_locale_name'], 'abc')
        self.assertEqual(result['pyramid.default_locale_name'], 'abc')
        result = self._makeOne({}, {'PYRAMID_DEFAULT_LOCALE_NAME':'abc'})
        self.assertEqual(result['default_locale_name'], 'abc')
        result = self._makeOne({'default_locale_name':'def'},
                             {'PYRAMID_DEFAULT_LOCALE_NAME':'abc'})
        self.assertEqual(result['pyramid.default_locale_name'], 'abc')
        result = self._makeOne({'default_locale_name':'def',
                                'pyramid.default_locale_name':'abc'})
        self.assertEqual(result['default_locale_name'], 'abc')
        self.assertEqual(result['pyramid.default_locale_name'], 'abc')
        result = self._makeOne({'default_locale_name':'def',
                                'pyramid.default_locale_name':'ghi'},
                               {'PYRAMID_DEFAULT_LOCALE_NAME':'abc'})
        self.assertEqual(result['default_locale_name'], 'abc')
        self.assertEqual(result['pyramid.default_locale_name'], 'abc')
    def test_originals_kept(self):
        result = self._makeOne({'a':'i am so a'})
pyramid/tests/test_static.py
@@ -190,11 +190,12 @@
        cleanUp()
    def _getTargetClass(self):
        from pyramid.view import static
        return static
        from pyramid.static import static_view
        return static_view
    def _makeOne(self, path, package_name=None):
        return self._getTargetClass()(path, package_name=package_name)
    def _makeOne(self, path, package_name=None, use_subpath=False):
        return self._getTargetClass()(path, package_name=package_name,
                                      use_subpath=use_subpath)
        
    def _makeEnviron(self, **extras):
        environ = {
@@ -207,10 +208,10 @@
        environ.update(extras)
        return environ
    def test_abspath(self):
    def test_abspath_subpath(self):
        import os.path
        path = os.path.dirname(__file__)
        view = self._makeOne(path)
        view = self._makeOne(path, use_subpath=True)
        context = DummyContext()
        request = DummyRequest()
        request.subpath = ['__init__.py']
@@ -219,9 +220,9 @@
        self.assertEqual(request.copied, True)
        self.assertEqual(response.directory, os.path.normcase(path))
    def test_relpath(self):
    def test_relpath_subpath(self):
        path = 'fixtures'
        view = self._makeOne(path)
        view = self._makeOne(path, use_subpath=True)
        context = DummyContext()
        request = DummyRequest()
        request.subpath = ['__init__.py']
@@ -233,8 +234,22 @@
        self.assertEqual(response.package_name, 'pyramid.tests')
        self.assertEqual(response.cache_max_age, 3600)
    def test_relpath_withpackage(self):
        view = self._makeOne('another:fixtures')
    def test_relpath_notsubpath(self):
        path = 'fixtures'
        view = self._makeOne(path)
        context = DummyContext()
        request = DummyRequest()
        request.subpath = ['__init__.py']
        request.environ = self._makeEnviron()
        response = view(context, request)
        self.assertTrue(not hasattr(request, 'copied'))
        self.assertEqual(response.root_resource, 'fixtures')
        self.assertEqual(response.resource_name, 'fixtures')
        self.assertEqual(response.package_name, 'pyramid.tests')
        self.assertEqual(response.cache_max_age, 3600)
    def test_relpath_withpackage_subpath(self):
        view = self._makeOne('another:fixtures', use_subpath=True)
        context = DummyContext()
        request = DummyRequest()
        request.subpath = ['__init__.py']
@@ -246,8 +261,9 @@
        self.assertEqual(response.package_name, 'another')
        self.assertEqual(response.cache_max_age, 3600)
    def test_relpath_withpackage_name(self):
        view = self._makeOne('fixtures', package_name='another')
    def test_relpath_withpackage_name_subpath(self):
        view = self._makeOne('fixtures', package_name='another',
                             use_subpath=True)
        context = DummyContext()
        request = DummyRequest()
        request.subpath = ['__init__.py']
@@ -259,8 +275,9 @@
        self.assertEqual(response.package_name, 'another')
        self.assertEqual(response.cache_max_age, 3600)
    def test_no_subpath_preserves_path_info_and_script_name(self):
        view = self._makeOne('fixtures', package_name='another')
    def test_no_subpath_preserves_path_info_and_script_name_subpath(self):
        view = self._makeOne('fixtures', package_name='another',
                             use_subpath=True)
        context = DummyContext()
        request = DummyRequest()
        request.subpath = ()
@@ -272,8 +289,9 @@
        self.assertEqual(request.environ['SCRIPT_NAME'],
                         '/script_name/path_info')
    def test_with_subpath_path_info_ends_with_slash(self):
        view = self._makeOne('fixtures', package_name='another')
    def test_with_subpath_path_info_ends_with_slash_subpath(self):
        view = self._makeOne('fixtures', package_name='another',
                             use_subpath=True)
        context = DummyContext()
        request = DummyRequest()
        request.subpath = ('subpath',)
@@ -284,7 +302,8 @@
        self.assertEqual(request.environ['SCRIPT_NAME'], '/path_info')
    def test_with_subpath_original_script_name_preserved(self):
        view = self._makeOne('fixtures', package_name='another')
        view = self._makeOne('fixtures', package_name='another',
                             use_subpath=True)
        context = DummyContext()
        request = DummyRequest()
        request.subpath = ('subpath',)
@@ -297,7 +316,8 @@
                         '/scriptname/path_info')
    def test_with_subpath_new_script_name_fixes_trailing_slashes(self):
        view = self._makeOne('fixtures', package_name='another')
        view = self._makeOne('fixtures', package_name='another',
                             use_subpath=True)
        context = DummyContext()
        request = DummyRequest()
        request.subpath = ('sub', 'path')
@@ -390,6 +410,7 @@
        self.assertEqual(inst.registrations, expected)
    def test_add_viewname(self):
        from pyramid.security import NO_PERMISSION_REQUIRED
        from pyramid.static import static_view
        config = DummyConfig()
        inst = self._makeOne(config)
@@ -397,8 +418,7 @@
        expected = [('view/', 'anotherpackage:path/', False)]
        self.assertEqual(inst.registrations, expected)
        self.assertEqual(config.route_args, ('view/', 'view/*subpath'))
        self.assertEqual(config.view_kw['permission'],
                         '__no_permission_required__')
        self.assertEqual(config.view_kw['permission'], NO_PERMISSION_REQUIRED)
        self.assertEqual(config.view_kw['view'].__class__, static_view)
        self.assertEqual(config.view_kw['view'].app.cache_max_age, 1)
pyramid/tests/test_tweens.py
New file
@@ -0,0 +1,324 @@
import unittest
class TestTweens(unittest.TestCase):
    def _makeOne(self):
        from pyramid.config import Tweens
        return Tweens()
    def test_add_explicit(self):
        tweens = self._makeOne()
        tweens.add_explicit('name', 'factory')
        self.assertEqual(tweens.explicit, [('name',  'factory')])
        tweens.add_explicit('name2', 'factory2')
        self.assertEqual(tweens.explicit, [('name',  'factory'),
                                           ('name2', 'factory2')])
    def test_add_implicit_noaliases(self):
        from pyramid.tweens import INGRESS
        tweens = self._makeOne()
        tweens.add_implicit('name', 'factory')
        self.assertEqual(tweens.names, ['name'])
        self.assertEqual(tweens.factories,
                         {'name':'factory'})
        self.assertEqual(tweens.alias_to_name['name'], 'name')
        self.assertEqual(tweens.name_to_alias['name'], 'name')
        self.assertEqual(tweens.order, [(INGRESS, 'name')])
        self.assertEqual(tweens.ingress_alias_names, ['name'])
        tweens.add_implicit('name2', 'factory2')
        self.assertEqual(tweens.names, ['name',  'name2'])
        self.assertEqual(tweens.factories,
                         {'name':'factory', 'name2':'factory2'})
        self.assertEqual(tweens.alias_to_name['name2'], 'name2')
        self.assertEqual(tweens.name_to_alias['name2'], 'name2')
        self.assertEqual(tweens.order,
                         [(INGRESS, 'name'), (INGRESS, 'name2')])
        self.assertEqual(tweens.ingress_alias_names, ['name', 'name2'])
        tweens.add_implicit('name3', 'factory3', over='name2')
        self.assertEqual(tweens.names,
                         ['name',  'name2', 'name3'])
        self.assertEqual(tweens.factories,
                         {'name':'factory', 'name2':'factory2',
                          'name3':'factory3'})
        self.assertEqual(tweens.alias_to_name['name3'], 'name3')
        self.assertEqual(tweens.name_to_alias['name3'], 'name3')
        self.assertEqual(tweens.order,
                         [(INGRESS, 'name'), (INGRESS, 'name2'),
                          ('name3', 'name2')])
        self.assertEqual(tweens.ingress_alias_names, ['name', 'name2'])
    def test_add_implicit_withaliases(self):
        from pyramid.tweens import INGRESS
        tweens = self._makeOne()
        tweens.add_implicit('name1', 'factory', alias='n1')
        self.assertEqual(tweens.names, ['name1'])
        self.assertEqual(tweens.factories,
                         {'name1':'factory'})
        self.assertEqual(tweens.alias_to_name['n1'], 'name1')
        self.assertEqual(tweens.name_to_alias['name1'], 'n1')
        self.assertEqual(tweens.order, [(INGRESS, 'n1')])
        self.assertEqual(tweens.ingress_alias_names, ['n1'])
        tweens.add_implicit('name2', 'factory2', alias='n2')
        self.assertEqual(tweens.names, ['name1',  'name2'])
        self.assertEqual(tweens.factories,
                         {'name1':'factory', 'name2':'factory2'})
        self.assertEqual(tweens.alias_to_name['n2'], 'name2')
        self.assertEqual(tweens.name_to_alias['name2'], 'n2')
        self.assertEqual(tweens.order,
                         [(INGRESS, 'n1'), (INGRESS, 'n2')])
        self.assertEqual(tweens.ingress_alias_names, ['n1', 'n2'])
        tweens.add_implicit('name3', 'factory3', alias='n3', over='name2')
        self.assertEqual(tweens.names,
                         ['name1',  'name2', 'name3'])
        self.assertEqual(tweens.factories,
                         {'name1':'factory', 'name2':'factory2',
                          'name3':'factory3'})
        self.assertEqual(tweens.alias_to_name['n3'], 'name3')
        self.assertEqual(tweens.name_to_alias['name3'], 'n3')
        self.assertEqual(tweens.order,
                         [(INGRESS, 'n1'), (INGRESS, 'n2'),
                          ('n3', 'name2')])
        self.assertEqual(tweens.ingress_alias_names, ['n1', 'n2'])
    def test___call___explicit(self):
        tweens = self._makeOne()
        def factory1(handler, registry):
            return handler
        def factory2(handler, registry):
            return '123'
        tweens.explicit = [('name', factory1), ('name', factory2)]
        self.assertEqual(tweens(None, None), '123')
    def test___call___implicit(self):
        tweens = self._makeOne()
        def factory1(handler, registry):
            return handler
        def factory2(handler, registry):
            return '123'
        tweens.names = ['name', 'name2']
        tweens.alias_to_name = {'name':'name', 'name2':'name2'}
        tweens.name_to_alias = {'name':'name', 'name2':'name2'}
        tweens.factories = {'name':factory1, 'name2':factory2}
        self.assertEqual(tweens(None, None), '123')
    def test___call___implicit_with_aliasnames_different_than_names(self):
        tweens = self._makeOne()
        def factory1(handler, registry):
            return handler
        def factory2(handler, registry):
            return '123'
        tweens.names = ['name', 'name2']
        tweens.alias_to_name = {'foo1':'name', 'foo2':'name2'}
        tweens.name_to_alias = {'name':'foo1', 'name2':'foo2'}
        tweens.factories = {'name':factory1, 'name2':factory2}
        self.assertEqual(tweens(None, None), '123')
    def test_implicit_ordering_1(self):
        tweens = self._makeOne()
        tweens.add_implicit('name1', 'factory1')
        tweens.add_implicit('name2', 'factory2')
        self.assertEqual(tweens.implicit(),
                         [
                             ('name2', 'factory2'),
                             ('name1', 'factory1'),
                             ])
    def test_implicit_ordering_2(self):
        from pyramid.tweens import MAIN
        tweens = self._makeOne()
        tweens.add_implicit('name1', 'factory1')
        tweens.add_implicit('name2', 'factory2', over=MAIN)
        self.assertEqual(tweens.implicit(),
                         [
                             ('name1', 'factory1'),
                             ('name2', 'factory2'),
                             ])
    def test_implicit_ordering_3(self):
        from pyramid.tweens import MAIN
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('auth', 'auth_factory', under='browserid')
        add('dbt', 'dbt_factory')
        add('retry', 'retry_factory', over='txnmgr', under='exceptionview')
        add('browserid', 'browserid_factory')
        add('txnmgr', 'txnmgr_factory', under='exceptionview')
        add('exceptionview', 'excview_factory', over=MAIN)
        self.assertEqual(tweens.implicit(),
                         [
                             ('browserid', 'browserid_factory'),
                             ('auth', 'auth_factory'),
                             ('dbt', 'dbt_factory'),
                             ('exceptionview', 'excview_factory'),
                             ('retry', 'retry_factory'),
                             ('txnmgr', 'txnmgr_factory'),
                             ])
    def test_implicit_ordering_4(self):
        from pyramid.tweens import MAIN
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('exceptionview', 'excview_factory', over=MAIN)
        add('auth', 'auth_factory', under='browserid')
        add('retry', 'retry_factory', over='txnmgr', under='exceptionview')
        add('browserid', 'browserid_factory')
        add('txnmgr', 'txnmgr_factory', under='exceptionview')
        add('dbt', 'dbt_factory')
        self.assertEqual(tweens.implicit(),
                         [
                             ('dbt', 'dbt_factory'),
                             ('browserid', 'browserid_factory'),
                             ('auth', 'auth_factory'),
                             ('exceptionview', 'excview_factory'),
                             ('retry', 'retry_factory'),
                             ('txnmgr', 'txnmgr_factory'),
                             ])
    def test_implicit_ordering_5(self):
        from pyramid.tweens import MAIN, INGRESS
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('exceptionview', 'excview_factory', over=MAIN)
        add('auth', 'auth_factory', under=INGRESS)
        add('retry', 'retry_factory', over='txnmgr', under='exceptionview')
        add('browserid', 'browserid_factory', under=INGRESS)
        add('txnmgr', 'txnmgr_factory', under='exceptionview', over=MAIN)
        add('dbt', 'dbt_factory')
        self.assertEqual(tweens.implicit(),
                         [
                             ('dbt', 'dbt_factory'),
                             ('browserid', 'browserid_factory'),
                             ('auth', 'auth_factory'),
                             ('exceptionview', 'excview_factory'),
                             ('retry', 'retry_factory'),
                             ('txnmgr', 'txnmgr_factory'),
                             ])
    def test_implicit_ordering_withaliases(self):
        from pyramid.tweens import MAIN
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('exceptionview', 'excview_factory', alias='e', over=MAIN)
        add('auth', 'auth_factory', under='b')
        add('retry', 'retry_factory', over='t', under='exceptionview')
        add('browserid', 'browserid_factory', alias='b')
        add('txnmgr', 'txnmgr_factory', alias='t', under='exceptionview')
        add('dbt', 'dbt_factory')
        self.assertEqual(tweens.implicit(),
                         [
                             ('dbt', 'dbt_factory'),
                             ('browserid', 'browserid_factory'),
                             ('auth', 'auth_factory'),
                             ('exceptionview', 'excview_factory'),
                             ('retry', 'retry_factory'),
                             ('txnmgr', 'txnmgr_factory'),
                          ])
    def test_implicit_ordering_withaliases2(self):
        from pyramid.tweens import MAIN
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('exceptionview', 'excview_factory', alias='e', over=MAIN)
        add('auth', 'auth_factory', alias='a', under='b')
        add('retry', 'retry_factory', alias='r', over='t', under='e')
        add('browserid', 'browserid_factory', alias='b')
        add('txnmgr', 'txnmgr_factory', alias='t', under='e')
        add('dbt', 'dbt_factory', alias='d')
        self.assertEqual(tweens.implicit(),
                         [
                             ('dbt', 'dbt_factory'),
                             ('browserid', 'browserid_factory'),
                             ('auth', 'auth_factory'),
                             ('exceptionview', 'excview_factory'),
                             ('retry', 'retry_factory'),
                             ('txnmgr', 'txnmgr_factory'),
                          ])
    def test_implicit_ordering_missing_partial(self):
        from pyramid.tweens import MAIN
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('exceptionview', 'excview_factory', over=MAIN)
        add('auth', 'auth_factory', under='browserid')
        add('retry', 'retry_factory', over='txnmgr', under='exceptionview')
        add('browserid', 'browserid_factory')
        add('dbt', 'dbt_factory')
        self.assertEqual(tweens.implicit(),
                         [
                             ('dbt', 'dbt_factory'),
                             ('browserid', 'browserid_factory'),
                             ('auth', 'auth_factory'),
                             ('exceptionview', 'excview_factory'),
                             ('retry', 'retry_factory'),
                             ])
    def test_implicit_ordering_missing_partial2(self):
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('dbt', 'dbt_factory')
        add('auth', 'auth_factory', under='browserid')
        add('retry', 'retry_factory', over='txnmgr', under='exceptionview')
        add('browserid', 'browserid_factory')
        self.assertEqual(tweens.implicit(),
                         [
                             ('retry', 'retry_factory'),
                             ('browserid', 'browserid_factory'),
                             ('auth', 'auth_factory'),
                             ('dbt', 'dbt_factory'),
                             ])
    def test_implicit_ordering_missing_partial3(self):
        from pyramid.tweens import MAIN
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('exceptionview', 'excview_factory', over=MAIN)
        add('retry', 'retry_factory', over='txnmgr', under='exceptionview')
        add('browserid', 'browserid_factory')
        self.assertEqual(tweens.implicit(),
                         [
                             ('browserid', 'browserid_factory'),
                             ('exceptionview', 'excview_factory'),
                             ('retry', 'retry_factory'),
                             ])
    def test_implicit_ordering_missing_partial_with_aliases(self):
        from pyramid.tweens import MAIN
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('exceptionview', 'excview_factory', alias='e', over=MAIN)
        add('retry', 'retry_factory', over='txnmgr', under='e')
        add('browserid', 'browserid_factory')
        self.assertEqual(tweens.implicit(),
                         [
                             ('browserid', 'browserid_factory'),
                             ('exceptionview', 'excview_factory'),
                             ('retry', 'retry_factory'),
                             ])
    def test_implicit_ordering_conflict_direct(self):
        from pyramid.tweens import CyclicDependencyError
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('browserid', 'browserid_factory')
        add('auth', 'auth_factory', over='browserid', under='browserid')
        self.assertRaises(CyclicDependencyError, tweens.implicit)
    def test_implicit_ordering_conflict_indirect(self):
        from pyramid.tweens import CyclicDependencyError
        tweens = self._makeOne()
        add = tweens.add_implicit
        add('browserid', 'browserid_factory')
        add('auth', 'auth_factory', over='browserid')
        add('dbt', 'dbt_factory', under='browserid', over='auth')
        self.assertRaises(CyclicDependencyError, tweens.implicit)
class TestCyclicDependencyError(unittest.TestCase):
    def _makeOne(self, cycles):
        from pyramid.tweens import CyclicDependencyError
        return CyclicDependencyError(cycles)
    def test___str__(self):
        exc = self._makeOne({'a':['c', 'd'], 'c':['a']})
        result = str(exc)
        self.assertTrue("'a' sorts over ['c', 'd']" in result)
        self.assertTrue("'c' sorts over ['a']" in result)
pyramid/tests/test_util.py
@@ -175,3 +175,76 @@
        self.assertEqual(typ.package, None)
        self.assertEqual(typ.package_name, None)
class Test_WeakOrderedSet(unittest.TestCase):
    def _makeOne(self):
        from pyramid.config import WeakOrderedSet
        return WeakOrderedSet()
    def test_ctor(self):
        wos = self._makeOne()
        self.assertEqual(len(wos), 0)
        self.assertEqual(wos.last, None)
    def test_add_item(self):
        wos = self._makeOne()
        reg = Dummy()
        wos.add(reg)
        self.assertEqual(list(wos), [reg])
        self.assert_(reg in wos)
        self.assertEqual(wos.last, reg)
    def test_add_multiple_items(self):
        wos = self._makeOne()
        reg1 = Dummy()
        reg2 = Dummy()
        wos.add(reg1)
        wos.add(reg2)
        self.assertEqual(len(wos), 2)
        self.assertEqual(list(wos), [reg1, reg2])
        self.assert_(reg1 in wos)
        self.assert_(reg2 in wos)
        self.assertEqual(wos.last, reg2)
    def test_add_duplicate_items(self):
        wos = self._makeOne()
        reg = Dummy()
        wos.add(reg)
        wos.add(reg)
        self.assertEqual(len(wos), 1)
        self.assertEqual(list(wos), [reg])
        self.assert_(reg in wos)
        self.assertEqual(wos.last, reg)
    def test_weakref_removal(self):
        wos = self._makeOne()
        reg = Dummy()
        wos.add(reg)
        wos.remove(reg)
        self.assertEqual(len(wos), 0)
        self.assertEqual(list(wos), [])
        self.assertEqual(wos.last, None)
    def test_last_updated(self):
        wos = self._makeOne()
        reg = Dummy()
        reg2 = Dummy()
        wos.add(reg)
        wos.add(reg2)
        wos.remove(reg2)
        self.assertEqual(len(wos), 1)
        self.assertEqual(list(wos), [reg])
        self.assertEqual(wos.last, reg)
    def test_empty(self):
        wos = self._makeOne()
        reg = Dummy()
        reg2 = Dummy()
        wos.add(reg)
        wos.add(reg2)
        wos.empty()
        self.assertEqual(len(wos), 0)
        self.assertEqual(list(wos), [])
        self.assertEqual(wos.last, None)
class Dummy(object):
    pass
pyramid/tests/test_view.py
@@ -121,19 +121,6 @@
                                 secure=True)
        self.assertEqual(iterable, ())
    def test_call_view_returns_iresponse_adaptable(self):
        from pyramid.response import Response
        request = self._makeRequest()
        context = self._makeContext()
        view = make_view('123')
        self._registerView(request.registry, view, 'registered')
        def str_response(s):
            return Response(s)
        request.registry.registerAdapter(str_response, (str,), IResponse)
        iterable = self._callFUT(context, request, name='registered',
                                 secure=True)
        self.assertEqual(iterable, ['123'])
    def test_call_view_registered_insecure_no_call_permissive(self):
        context = self._makeContext()
        request = self._makeRequest()
@@ -408,6 +395,35 @@
        self.assertEqual(len(settings), 1)
        self.assertEqual(settings[0]['renderer'], {'a':1})
    def test_call_with_renderer_IRendererInfo(self):
        # see https://github.com/Pylons/pyramid/pull/234
        from pyramid.interfaces import IRendererInfo
        import pyramid.tests
        outerself = self
        class DummyRendererHelper(object):
            implements(IRendererInfo)
            name = 'fixtures/minimal.pt'
            package = pyramid.tests
            def clone(self, name=None, package=None, registry=None):
                outerself.assertEqual(name, self.name)
                outerself.assertEqual(package, self.package)
                outerself.assertEqual(registry, context.config.registry)
                self.cloned = True
                return self
        renderer_helper = DummyRendererHelper()
        decorator = self._makeOne(renderer=renderer_helper)
        venusian = DummyVenusian()
        decorator.venusian = venusian
        def foo(): pass
        wrapped = decorator(foo)
        self.assertTrue(wrapped is foo)
        context = DummyVenusianContext()
        settings = call_venusian(venusian, context)
        self.assertEqual(len(settings), 1)
        renderer = settings[0]['renderer']
        self.assertTrue(renderer is renderer_helper)
        self.assertTrue(renderer.cloned)
class Test_append_slash_notfound_view(BaseTest, unittest.TestCase):
    def _callFUT(self, context, request):
        from pyramid.view import append_slash_notfound_view
@@ -554,6 +570,48 @@
        result = self._callFUT(module)
        self.assertEqual(result, False)
class Test_static(unittest.TestCase):
    def setUp(self):
        from zope.deprecation import __show__
        __show__.off()
    def tearDown(self):
        from zope.deprecation import __show__
        __show__.on()
    def _getTargetClass(self):
        from pyramid.view import static
        return static
    def _makeOne(self, path, package_name=None):
        return self._getTargetClass()(path, package_name=package_name)
    def _makeEnviron(self, **extras):
        environ = {
            'wsgi.url_scheme':'http',
            'wsgi.version':(1,0),
            'SERVER_NAME':'localhost',
            'SERVER_PORT':'8080',
            'REQUEST_METHOD':'GET',
            }
        environ.update(extras)
        return environ
    def test_relpath_subpath(self):
        path = 'fixtures'
        view = self._makeOne(path)
        context = DummyContext()
        request = DummyRequest()
        request.subpath = ['__init__.py']
        request.environ = self._makeEnviron()
        response = view(context, request)
        self.assertEqual(request.copied, True)
        self.assertEqual(response.root_resource, 'fixtures')
        self.assertEqual(response.resource_name, 'fixtures')
        self.assertEqual(response.package_name, 'pyramid.tests')
        self.assertEqual(response.cache_max_age, 3600)
class ExceptionResponse(Exception):
    status = '404 Not Found'
    app_iter = ['Not Found']
@@ -569,6 +627,18 @@
class DummyRequest:
    exception = None
    def __init__(self, environ=None):
        if environ is None:
            environ = {}
        self.environ = environ
    def get_response(self, application):
        return application
    def copy(self):
        self.copied = True
        return self
from pyramid.interfaces import IResponse
from zope.interface import implements
@@ -620,8 +690,9 @@
    def __init__(self):
        self.config = DummyConfig()
        
def call_venusian(venusian):
    context = DummyVenusianContext()
def call_venusian(venusian, context=None):
    if context is None:
        context = DummyVenusianContext()
    for wrapped, callback, category in venusian.attachments:
        callback(context, None, None)
    return context.config.settings
pyramid/tests/venusianapp/__init__.py
New file
@@ -0,0 +1,14 @@
import venusian
def foo(wrapped):
    def bar(scanner, name, wrapped):
        scanner.config.a = scanner.a
    venusian.attach(wrapped, bar)
    return wrapped
@foo
def hello():
    pass
hello() # appease coverage
pyramid/tweens.py
New file
@@ -0,0 +1,198 @@
import sys
from pyramid.exceptions import ConfigurationError
from pyramid.interfaces import IExceptionViewClassifier
from pyramid.interfaces import IView
from pyramid.interfaces import ITweens
from zope.interface import providedBy
from zope.interface import implements
def excview_tween_factory(handler, registry):
    """ A :term:`tween` factory which produces a tween that catches an
    exception raised by downstream tweens (or the main Pyramid request
    handler) and, if possible, converts it into a Response using an
    :term:`exception view`."""
    adapters = registry.adapters
    def excview_tween(request):
        attrs = request.__dict__
        try:
            response = handler(request)
        except Exception, exc:
            # WARNING: do not assign the result of sys.exc_info() to a
            # local var here, doing so will cause a leak
            attrs['exc_info'] = sys.exc_info()
            attrs['exception'] = exc
            # clear old generated request.response, if any; it may
            # have been mutated by the view, and its state is not
            # sane (e.g. caching headers)
            if 'response' in attrs:
                del attrs['response']
            request_iface = attrs['request_iface']
            provides = providedBy(exc)
            for_ = (IExceptionViewClassifier, request_iface.combined, provides)
            view_callable = adapters.lookup(for_, IView, default=None)
            if view_callable is None:
                raise
            response = view_callable(exc, request)
        finally:
            # prevent leakage (wrt exc_info)
            if 'exc_info' in attrs:
                del attrs['exc_info']
            if 'exception' in attrs:
                del attrs['exception']
        return response
    return excview_tween
class CyclicDependencyError(Exception):
    def __init__(self, cycles):
        self.cycles = cycles
    def __str__(self):
        L = []
        cycles = self.cycles
        for cycle in cycles:
            dependent = cycle
            dependees = cycles[cycle]
            L.append('%r sorts over %r' % (dependent, dependees))
        msg = 'Implicit tween ordering cycle:' + '; '.join(L)
        return msg
class Tweens(object):
    implements(ITweens)
    def __init__(self):
        self.explicit = []
        self.names = []
        self.factories = {}
        self.order = []
        self.ingress_alias_names = []
        self.alias_to_name = {INGRESS:INGRESS, MAIN:MAIN}
        self.name_to_alias = {INGRESS:INGRESS, MAIN:MAIN}
    def add_explicit(self, name, factory):
        self.explicit.append((name, factory))
    def add_implicit(self, name, factory, alias=None, under=None, over=None):
        if alias is None:
            alias = name
        self.alias_to_name[alias] = name
        self.name_to_alias[name] = alias
        self.names.append(name)
        self.factories[name] = factory
        if under is None and over is None:
            under = INGRESS
            self.ingress_alias_names.append(alias)
        if under is not None:
            self.order.append((under, alias))
        if over is not None:
            self.order.append((alias, over))
    def implicit(self):
        order = [(INGRESS, MAIN)]
        roots = []
        graph = {}
        has_order = {}
        aliases = [INGRESS, MAIN]
        ingress_alias_names = self.ingress_alias_names[:]
        for name in self.names:
            aliases.append(self.name_to_alias[name])
        for a, b in self.order:
            # try to convert both a and b to an alias
            a = self.name_to_alias.get(a, a)
            b = self.name_to_alias.get(b, b)
            order.append((a, b))
        def add_node(node):
            if not graph.has_key(node):
                roots.append(node)
                graph[node] = [0] # 0 = number of arcs coming into this node
        def add_arc(fromnode, tonode):
            graph[fromnode].append(tonode)
            graph[tonode][0] += 1
            if tonode in roots:
                roots.remove(tonode)
        # remove ordering information that mentions unknown names/aliases
        for pos, (first, second) in enumerate(order):
            has_first = first in aliases
            has_second = second in aliases
            if (not has_first) or (not has_second):
                order[pos] = None, None
            else:
                has_order[first] = has_order[second] = True
        for alias in aliases:
            # any alias that doesn't have an ordering after we detect all
            # nodes with orders should get an ordering relative to INGRESS,
            # as if it were added with no under or over in add_implicit
            if (not alias in has_order) and (alias not in (INGRESS, MAIN)):
                order.append((INGRESS, alias))
                ingress_alias_names.append(alias)
            add_node(alias)
        for a, b in order:
            if a is not None and b is not None: # deal with removed orders
                add_arc(a, b)
        sorted_aliases = []
        while roots:
            root = roots.pop(0)
            sorted_aliases.append(root)
            children = graph[root][1:]
            for child in children:
                arcs = graph[child][0]
                arcs -= 1
                graph[child][0] = arcs
                if arcs == 0:
                    roots.insert(0, child)
            del graph[root]
        if graph:
            # loop in input
            cycledeps = {}
            for k, v in graph.items():
                cycledeps[k] = v[1:]
            raise CyclicDependencyError(cycledeps)
        result = []
        for alias in sorted_aliases:
            if alias not in (MAIN, INGRESS):
                name = self.alias_to_name.get(alias, alias)
                result.append((name, self.factories[name]))
        return result
    def __call__(self, handler, registry):
        if self.explicit:
            use = self.explicit
        else:
            use = self.implicit()
        for name, factory in use[::-1]:
            handler = factory(handler, registry)
        return handler
def tween_factory_name(factory):
    if (hasattr(factory, '__name__') and hasattr(factory, '__module__')):
        # function or class
        name = '.'.join([factory.__module__, factory.__name__])
    elif hasattr(factory, '__module__'):
        # instance
        name = '.'.join([factory.__module__, factory.__class__.__name__,
                         str(id(factory))])
    else:
        raise ConfigurationError(
            'A tween factory must be a class, an instance, or a function; '
            '%s is not a suitable tween factory' % factory)
    return name
MAIN = 'MAIN'
INGRESS = 'INGRESS'
EXCVIEW = 'excview'
pyramid/url.py
@@ -404,7 +404,7 @@
    ``/foo/1``.
    If the 'current route' has the route pattern ``/foo/{page}`` and the
    current current url path is ``/foo/1``, the matchdict will be
    current url path is ``/foo/1``, the matchdict will be
    ``{'page':'1'}``.  The result of ``current_route_url(request, page='2')``
    in this situation will be ``/foo/2``.
        
pyramid/urldispatch.py
@@ -5,12 +5,10 @@
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import IRoute
from pyramid.compat import all
from pyramid.encode import url_quote
from pyramid.exceptions import URLDecodeError
from pyramid.traversal import traversal_path
from pyramid.traversal import quote_path_segment
_marker = object()
pyramid/util.py
@@ -1,5 +1,6 @@
import pkg_resources
import sys
import weakref
from pyramid.exceptions import ConfigurationError
from pyramid.path import package_of
@@ -143,4 +144,66 @@
                return self._zope_dottedname_style(dotted)
        return dotted
class WeakOrderedSet(object):
    """ Maintain a set of items.
    Each item is stored as a weakref to avoid extending their lifetime.
    The values may be iterated over or the last item added may be
    accessed via the ``last`` property.
    If items are added more than once, the most recent addition will
    be remembered in the order:
        order = WeakOrderedSet()
        order.add('1')
        order.add('2')
        order.add('1')
        list(order) == ['2', '1']
        order.last == '1'
    """
    def __init__(self):
        self._items = {}
        self._order = []
    def add(self, item):
        """ Add an item to the set."""
        oid = id(item)
        if oid in self._items:
            self._order.remove(oid)
            self._order.append(oid)
            return
        ref = weakref.ref(item, lambda x: self.remove(item))
        self._items[oid] = ref
        self._order.append(oid)
    def remove(self, item):
        """ Remove an item from the set."""
        oid = id(item)
        if oid in self._items:
            del self._items[oid]
            self._order.remove(oid)
    def empty(self):
        """ Clear all objects from the set."""
        self._items = {}
        self._order = []
    def __len__(self):
        return len(self._order)
    def __contains__(self, item):
        oid = id(item)
        return oid in self._items
    def __iter__(self):
        return (self._items[oid]() for oid in self._order)
    @property
    def last(self):
        if self._order:
            oid = self._order[-1]
            return self._items[oid]()
pyramid/view.py
@@ -4,13 +4,14 @@
from zope.interface import providedBy
from zope.deprecation import deprecated
from pyramid.interfaces import IResponse
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
from pyramid.interfaces import IRendererInfo
from pyramid.httpexceptions import HTTPFound
from pyramid.httpexceptions import default_exceptionresponse_view
from pyramid.path import caller_package
from pyramid.renderers import RendererHelper
from pyramid.static import static_view
from pyramid.threadlocal import get_current_registry
@@ -30,11 +31,26 @@
# fallout.
init_mimetypes(mimetypes)
# Nasty BW compat hack: dont yet deprecate this (ever?)
class static(static_view): # only subclass for purposes of autodoc
    __doc__ = static_view.__doc__
_marker = object()
class static(static_view):
    """ Backwards compatibility alias for
    :class:`pyramid.static.static_view`; it overrides that class' constructor
    to pass ``use_subpath=True`` by default.  This class is deprecated as of
    :app:`Pyramid` 1.1.  Use :class:`pyramid.static.static_view` instead
    (probably with a ``use_subpath=True`` argument).
    """
    def __init__(self, root_dir, cache_max_age=3600, package_name=None):
        if package_name is None:
            package_name = caller_package().__name__
        static_view.__init__(self, root_dir, cache_max_age=cache_max_age,
                             package_name=package_name, use_subpath=True)
deprecated(
    'static',
    'The "pyramid.view.static" class is deprecated as of Pyramid 1.1; '
    'use the "pyramid.static.static_view" class instead with the '
    '"use_subpath" argument set to True.')
def render_view_to_response(context, request, name='', secure=True):
    """ Call the :term:`view callable` configured with a :term:`view
@@ -101,11 +117,6 @@
    response = render_view_to_response(context, request, name, secure)
    if response is None:
        return None
    try:
        reg = request.registry
    except AttributeError:
        reg = get_current_registry()
    response = reg.queryAdapterOrSelf(response, IResponse)
    return response.app_iter
def render_view(context, request, name='', secure=True):
@@ -211,6 +222,12 @@
                renderer = RendererHelper(name=renderer,
                                          package=info.module,
                                          registry=context.config.registry)
            elif IRendererInfo.providedBy(renderer):
                # create a new rendererinfo to clear out old registry on a
                # rescan, see https://github.com/Pylons/pyramid/pull/234
                renderer = renderer.clone(name=renderer.name,
                                          package=info.module,
                                          registry=context.config.registry)
            settings['renderer'] = renderer
            context.config.add_view(view=ob, **settings)
pyramid/wsgi.py
@@ -1,4 +1,4 @@
from pyramid.compat import wraps
from functools import wraps
from pyramid.request import call_app_with_subpath_as_path_info
def wsgiapp(wrapped):
setup.py
@@ -53,7 +53,7 @@
    install_requires.append('simplejson')
    
setup(name='pyramid',
      version='1.1a4',
      version='1.1.1dev',
      description=('The Pyramid web application development framework, a '
                   'Pylons project'),
      long_description=README + '\n\n' +  CHANGES,
@@ -86,6 +86,7 @@
        pshell=pyramid.paster:PShellCommand
        proutes=pyramid.paster:PRoutesCommand
        pviews=pyramid.paster:PViewsCommand
        ptweens=pyramid.paster:PTweensCommand
        [console_scripts]
        bfg2pyramid = pyramid.fixers.fix_bfg_imports:main
      """
tox.ini
@@ -26,7 +26,7 @@
    repoze.sphinx.autointerface
    virtualenv
    nose
    coverage
    coverage==3.4
    nosexcover
# we separate coverage into its own testenv because a) "last run wins" wrt