summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-08-10 20:20:05 -0400
committerChris McDonough <chrism@plope.com>2011-08-10 20:20:05 -0400
commit995466c6bc0da04f50d2db83af653362a0dadd6f (patch)
treee5e57108a3d751d9e02cbf06ad5eca8902f4a100
parent9a8ba2f09fe3791febbfec2ac383c091aacfbf5b (diff)
parent3e3fcdf1376218a4fa6dcffec4f27a41c63d1675 (diff)
downloadpyramid-995466c6bc0da04f50d2db83af653362a0dadd6f.tar.gz
pyramid-995466c6bc0da04f50d2db83af653362a0dadd6f.tar.bz2
pyramid-995466c6bc0da04f50d2db83af653362a0dadd6f.zip
fix merge conflicts
-rw-r--r--CHANGES.txt345
-rw-r--r--CONTRIBUTORS.txt4
-rw-r--r--LICENSE.txt53
-rw-r--r--RELEASING.txt33
-rw-r--r--TODO.txt64
-rw-r--r--docs/api.rst2
-rw-r--r--docs/api/config.rst16
-rw-r--r--docs/api/interfaces.rst5
-rw-r--r--docs/api/paster.rst14
-rw-r--r--docs/api/renderers.rst9
-rw-r--r--docs/api/request.rst21
-rw-r--r--docs/api/response.rst5
-rw-r--r--docs/api/scripting.rst2
-rw-r--r--docs/api/security.rst2
-rw-r--r--docs/api/static.rst11
-rw-r--r--docs/api/tweens.rst25
-rw-r--r--docs/authorintro.rst6
-rw-r--r--docs/conf.py2
-rw-r--r--docs/copyright.rst8
-rw-r--r--docs/designdefense.rst77
-rw-r--r--docs/glossary.rst32
-rw-r--r--docs/index.rst17
-rw-r--r--docs/latexindex.rst15
-rwxr-xr-xdocs/make_book2
-rwxr-xr-xdocs/make_epub2
-rwxr-xr-xdocs/make_pdf4
-rw-r--r--docs/narr/MyProject/development.ini15
-rw-r--r--docs/narr/MyProject/myproject/templates/mytemplate.pt14
-rw-r--r--docs/narr/MyProject/production.ini13
-rw-r--r--docs/narr/MyProject/setup.py2
-rw-r--r--docs/narr/advconfig.rst9
-rw-r--r--docs/narr/assets.rst34
-rw-r--r--docs/narr/commandline.rst560
-rw-r--r--docs/narr/configuration.rst75
-rw-r--r--docs/narr/environment.rst171
-rw-r--r--docs/narr/firstapp.rst13
-rw-r--r--docs/narr/hooks.rst301
-rw-r--r--docs/narr/hybrid.rst30
-rw-r--r--docs/narr/i18n.rst41
-rw-r--r--docs/narr/install.rst41
-rw-r--r--docs/narr/introduction.rst85
-rw-r--r--docs/narr/muchadoabouttraversal.rst15
-rw-r--r--docs/narr/project-debug.pngbin0 -> 153807 bytes
-rw-r--r--docs/narr/project.pngbin84679 -> 128727 bytes
-rw-r--r--docs/narr/project.rst255
-rw-r--r--docs/narr/renderers.rst28
-rw-r--r--docs/narr/resources.rst18
-rw-r--r--docs/narr/router.rst1
-rw-r--r--docs/narr/security.rst19
-rw-r--r--docs/narr/sessions.rst26
-rw-r--r--docs/narr/startup.rst30
-rw-r--r--docs/narr/templates.rst30
-rw-r--r--docs/narr/traversal.rst97
-rw-r--r--docs/narr/urldispatch.rst297
-rw-r--r--docs/narr/vhosting.rst3
-rw-r--r--docs/narr/viewconfig.rst396
-rw-r--r--docs/narr/views.rst237
-rw-r--r--docs/narr/webob.rst90
-rw-r--r--docs/narr/zca.rst1
-rwxr-xr-xdocs/remake1
-rw-r--r--docs/tutorials/modwsgi/index.rst3
-rw-r--r--docs/tutorials/wiki/src/authorization/development.ini23
-rw-r--r--docs/tutorials/wiki/src/authorization/production.ini21
-rw-r--r--docs/tutorials/wiki/src/authorization/setup.py4
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css4
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt17
-rw-r--r--docs/tutorials/wiki/src/basiclayout/development.ini23
-rw-r--r--docs/tutorials/wiki/src/basiclayout/production.ini21
-rw-r--r--docs/tutorials/wiki/src/basiclayout/setup.py4
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css4
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt17
-rw-r--r--docs/tutorials/wiki/src/models/development.ini23
-rw-r--r--docs/tutorials/wiki/src/models/production.ini21
-rw-r--r--docs/tutorials/wiki/src/models/setup.py4
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/pylons.css4
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt17
-rw-r--r--docs/tutorials/wiki/src/tests/development.ini23
-rw-r--r--docs/tutorials/wiki/src/tests/production.ini21
-rw-r--r--docs/tutorials/wiki/src/tests/setup.py4
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/pylons.css4
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt17
-rw-r--r--docs/tutorials/wiki/src/views/development.ini23
-rw-r--r--docs/tutorials/wiki/src/views/production.ini21
-rw-r--r--docs/tutorials/wiki/src/views/setup.py4
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/pylons.css4
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt17
-rw-r--r--docs/tutorials/wiki2/authorization.rst4
-rw-r--r--docs/tutorials/wiki2/basiclayout.rst2
-rw-r--r--docs/tutorials/wiki2/src/authorization/development.ini12
-rw-r--r--docs/tutorials/wiki2/src/authorization/production.ini12
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models.py2
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css4
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt17
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/development.ini12
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/production.ini12
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/models.py2
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css4
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt17
-rw-r--r--docs/tutorials/wiki2/src/models/development.ini12
-rw-r--r--docs/tutorials/wiki2/src/models/production.ini12
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/models.py2
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/pylons.css4
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt17
-rw-r--r--docs/tutorials/wiki2/src/tests/development.ini12
-rw-r--r--docs/tutorials/wiki2/src/tests/production.ini12
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/models.py2
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css4
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt17
-rw-r--r--docs/tutorials/wiki2/src/views/development.ini12
-rw-r--r--docs/tutorials/wiki2/src/views/production.ini12
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/models.py2
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/pylons.css4
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt17
-rw-r--r--docs/whatsnew-1.0.rst4
-rw-r--r--docs/whatsnew-1.1.rst263
-rw-r--r--pyramid/compat.py8
-rw-r--r--pyramid/compat/__init__.py157
-rw-r--r--pyramid/config.py406
-rw-r--r--pyramid/events.py57
-rw-r--r--pyramid/httpexceptions.py5
-rw-r--r--pyramid/i18n.py5
-rw-r--r--pyramid/interfaces.py37
-rw-r--r--pyramid/log.py16
-rw-r--r--pyramid/paster.py370
-rw-r--r--pyramid/renderers.py61
-rw-r--r--pyramid/request.py21
-rw-r--r--pyramid/response.py59
-rw-r--r--pyramid/router.py254
-rwxr-xr-xpyramid/scaffolds/alchemy/+package+/models.py2
-rw-r--r--pyramid/scaffolds/alchemy/+package+/static/pylons.css2
-rw-r--r--pyramid/scaffolds/alchemy/+package+/templates/model.pt_tmpl18
-rw-r--r--pyramid/scaffolds/alchemy/+package+/templates/root.pt_tmpl18
-rw-r--r--pyramid/scaffolds/alchemy/development.ini_tmpl22
-rw-r--r--pyramid/scaffolds/alchemy/production.ini_tmpl20
-rw-r--r--pyramid/scaffolds/alchemy/setup.py_tmpl3
-rw-r--r--pyramid/scaffolds/routesalchemy/+package+/models.py2
-rw-r--r--pyramid/scaffolds/routesalchemy/+package+/static/pylons.css2
-rw-r--r--pyramid/scaffolds/routesalchemy/+package+/templates/mytemplate.pt_tmpl18
-rw-r--r--pyramid/scaffolds/routesalchemy/development.ini_tmpl22
-rw-r--r--pyramid/scaffolds/routesalchemy/production.ini_tmpl20
-rw-r--r--pyramid/scaffolds/routesalchemy/setup.py_tmpl3
-rw-r--r--pyramid/scaffolds/starter/+package+/static/pylons.css2
-rw-r--r--pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl18
-rw-r--r--pyramid/scaffolds/starter/development.ini_tmpl15
-rw-r--r--pyramid/scaffolds/starter/production.ini_tmpl13
-rw-r--r--pyramid/scaffolds/starter/setup.py_tmpl2
-rw-r--r--pyramid/scaffolds/tests.py43
-rw-r--r--pyramid/scaffolds/zodb/+package+/static/pylons.css2
-rw-r--r--pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl18
-rw-r--r--pyramid/scaffolds/zodb/development.ini_tmpl24
-rw-r--r--pyramid/scaffolds/zodb/production.ini_tmpl22
-rw-r--r--pyramid/scaffolds/zodb/setup.py_tmpl4
-rw-r--r--pyramid/scripting.py95
-rw-r--r--pyramid/security.py2
-rw-r--r--pyramid/settings.py39
-rw-r--r--pyramid/static.py21
-rw-r--r--pyramid/tests/defpermbugapp/__init__.py3
-rw-r--r--pyramid/tests/grokkedapp/__init__.py34
-rw-r--r--pyramid/tests/grokkedapp/another.py24
-rw-r--r--pyramid/tests/grokkedapp/pod/notinit.py3
-rw-r--r--pyramid/tests/grokkedapp/subpackage/__init__.py3
-rw-r--r--pyramid/tests/grokkedapp/subpackage/notinit.py3
-rw-r--r--pyramid/tests/grokkedapp/subpackage/subsubpackage/__init__.py3
-rw-r--r--pyramid/tests/restbugapp/views.py2
-rw-r--r--pyramid/tests/test_compat.py9
-rw-r--r--pyramid/tests/test_config.py738
-rw-r--r--pyramid/tests/test_events.py30
-rw-r--r--pyramid/tests/test_httpexceptions.py11
-rw-r--r--pyramid/tests/test_i18n.py19
-rw-r--r--pyramid/tests/test_integration.py6
-rw-r--r--pyramid/tests/test_log.py16
-rw-r--r--pyramid/tests/test_paster.py686
-rw-r--r--pyramid/tests/test_renderers.py63
-rw-r--r--pyramid/tests/test_request.py34
-rw-r--r--pyramid/tests/test_response.py64
-rw-r--r--pyramid/tests/test_router.py118
-rw-r--r--pyramid/tests/test_scripting.py114
-rw-r--r--pyramid/tests/test_settings.py281
-rw-r--r--pyramid/tests/test_static.py60
-rw-r--r--pyramid/tests/test_tweens.py324
-rw-r--r--pyramid/tests/test_util.py73
-rw-r--r--pyramid/tests/test_view.py101
-rw-r--r--pyramid/tests/venusianapp/__init__.py14
-rw-r--r--pyramid/tweens.py198
-rw-r--r--pyramid/url.py2
-rw-r--r--pyramid/urldispatch.py2
-rw-r--r--pyramid/util.py63
-rw-r--r--pyramid/view.py37
-rw-r--r--pyramid/wsgi.py2
-rw-r--r--setup.py3
-rw-r--r--tox.ini2
191 files changed, 6783 insertions, 2870 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 0eb02baad..208662150 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,10 +4,324 @@ Next release
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 @@ Features
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)
==================
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index a9ef27c28..a368fb4d2 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -144,3 +144,7 @@ Contributors
- Christoph Zwerschke, 2011/06/07
- Atsushi Odagiri, 2011/07/02
+
+- Shane Hathaway, 2011/07/22
+
+- Manuel Hermann, 2011/07/11
diff --git a/LICENSE.txt b/LICENSE.txt
index 28824ee3f..f7ace1698 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -98,59 +98,6 @@ under the ZPL):
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
diff --git a/RELEASING.txt b/RELEASING.txt
index 645083acf..3116d2643 100644
--- a/RELEASING.txt
+++ b/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 @@ Releasing Pyramid
- 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
diff --git a/TODO.txt b/TODO.txt
index fc64163e1..42de96a38 100644
--- a/TODO.txt
+++ b/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,15 +91,16 @@ Nice-to-Have
- 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``.
- Consider implementing the API outlined in
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.
diff --git a/docs/api.rst b/docs/api.rst
index be7942502..6ff6e9fb1 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -28,9 +28,11 @@ documentation is organized alphabetically by module name.
api/security
api/session
api/settings
+ api/static
api/testing
api/threadlocal
api/traversal
+ api/tweens
api/url
api/view
api/wsgi
diff --git a/docs/api/config.rst b/docs/api/config.rst
index 71ef4a746..30c541905 100644
--- a/docs/api/config.rst
+++ b/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.
+
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index 51a1963b5..9ab9eefc3 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -9,14 +9,19 @@ Event-Related Interfaces
++++++++++++++++++++++++
.. autointerface:: IApplicationCreated
+ :members:
.. autointerface:: INewRequest
+ :members:
.. autointerface:: IContextFound
+ :members:
.. autointerface:: INewResponse
+ :members:
.. autointerface:: IBeforeRender
+ :members:
Other Interfaces
++++++++++++++++
diff --git a/docs/api/paster.rst b/docs/api/paster.rst
index 9ecfa3d9c..2a32e07e9 100644
--- a/docs/api/paster.rst
+++ b/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
diff --git a/docs/api/renderers.rst b/docs/api/renderers.rst
index c13694219..15670c46e 100644
--- a/docs/api/renderers.rst
+++ b/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).
+
diff --git a/docs/api/request.rst b/docs/api/request.rst
index 5dfb2ae9a..2ab3977d5 100644
--- a/docs/api/request.rst
+++ b/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::
diff --git a/docs/api/response.rst b/docs/api/response.rst
index e67b15568..8020b629a 100644
--- a/docs/api/response.rst
+++ b/docs/api/response.rst
@@ -9,3 +9,8 @@
:members:
:inherited-members:
+Functions
+~~~~~~~~~
+
+.. autofunction:: response_adapter
+
diff --git a/docs/api/scripting.rst b/docs/api/scripting.rst
index 9d5bc2e58..51bd3c7a0 100644
--- a/docs/api/scripting.rst
+++ b/docs/api/scripting.rst
@@ -7,3 +7,5 @@
.. autofunction:: get_root
+ .. autofunction:: prepare
+
diff --git a/docs/api/security.rst b/docs/api/security.rst
index de249355d..8cd9e5dae 100644
--- a/docs/api/security.rst
+++ b/docs/api/security.rst
@@ -57,6 +57,8 @@ Constants
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
-------------
diff --git a/docs/api/static.rst b/docs/api/static.rst
new file mode 100644
index 000000000..c28473584
--- /dev/null
+++ b/docs/api/static.rst
@@ -0,0 +1,11 @@
+.. _static_module:
+
+:mod:`pyramid.static`
+---------------------
+
+.. automodule:: pyramid.static
+
+ .. autoclass:: static_view
+ :members:
+ :inherited-members:
+
diff --git a/docs/api/tweens.rst b/docs/api/tweens.rst
new file mode 100644
index 000000000..ddacd2cde
--- /dev/null
+++ b/docs/api/tweens.rst
@@ -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`.
diff --git a/docs/authorintro.rst b/docs/authorintro.rst
index fb34f1d34..f1a9d1484 100644
--- a/docs/authorintro.rst
+++ b/docs/authorintro.rst
@@ -165,6 +165,7 @@ others' technology.
single: Shipman, John
single: Beelby, Chris
single: Paez, Patricio
+ single: Merickel, Michael
Thanks
======
@@ -179,8 +180,9 @@ software which it details would exist: Paul Everitt, Tres Seaver, Andrew
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.
diff --git a/docs/conf.py b/docs/conf.py
index f09cd63f5..5eacb257f 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -93,7 +93,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year
# 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
diff --git a/docs/copyright.rst b/docs/copyright.rst
index 1c7aa5844..4369a7a3f 100644
--- a/docs/copyright.rst
+++ b/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 @@ Contributors:
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/>`_.
diff --git a/docs/designdefense.rst b/docs/designdefense.rst
index ce3c507c5..4975ae2a0 100644
--- a/docs/designdefense.rst
+++ b/docs/designdefense.rst
@@ -711,9 +711,9 @@ Pyramid Has Too Many Dependencies
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 @@ Self-described "microframeworks" exist: `Bottle <http://bottle.paws.de>`_ and
<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 @@ predictability.
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,20 +1471,52 @@ the view associated with the ``/:action`` routing pattern will be invoked: it
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.
-
-Does that make you a little uncomfortable? It should, because
+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.
+
+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 @@ If you can understand this hello world program, you can use Pyramid:
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
+++++++++
diff --git a/docs/glossary.rst b/docs/glossary.rst
index e45317dae..f0ad81ded 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -23,6 +23,11 @@ Glossary
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 @@ Glossary
: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 @@ Glossary
`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 @@ Glossary
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 @@ Glossary
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`.
+
diff --git a/docs/index.rst b/docs/index.rst
index a4743af9b..85e733dc3 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -24,8 +24,8 @@ Front Matter
.. toctree::
:maxdepth: 1
- whatsnew-1.0
whatsnew-1.1
+ whatsnew-1.0
Narrative documentation
=======================
@@ -43,23 +43,24 @@ Narrative documentation in chapter form explaining how to use
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
diff --git a/docs/latexindex.rst b/docs/latexindex.rst
index a4926bf30..bdd923dbc 100644
--- a/docs/latexindex.rst
+++ b/docs/latexindex.rst
@@ -32,23 +32,24 @@ Narrative Documentation
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
diff --git a/docs/make_book b/docs/make_book
index dc8381845..22883ac4a 100755
--- a/docs/make_book
+++ b/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
diff --git a/docs/make_epub b/docs/make_epub
new file mode 100755
index 000000000..868749e3b
--- /dev/null
+++ b/docs/make_epub
@@ -0,0 +1,2 @@
+#!/bin/sh
+make clean epub SPHINXBUILD=../env26/bin/sphinx-build
diff --git a/docs/make_pdf b/docs/make_pdf
new file mode 100755
index 000000000..558feb857
--- /dev/null
+++ b/docs/make_pdf
@@ -0,0 +1,4 @@
+#!/bin/sh
+make clean latex SPHINXBUILD=../env26/bin/sphinx-build
+cd _build/latex && make all-pdf
+
diff --git a/docs/narr/MyProject/development.ini b/docs/narr/MyProject/development.ini
index 29486ce56..1818e387b 100644
--- a/docs/narr/MyProject/development.ini
+++ b/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]
diff --git a/docs/narr/MyProject/myproject/templates/mytemplate.pt b/docs/narr/MyProject/myproject/templates/mytemplate.pt
index 632c34876..97f1e1aa3 100644
--- a/docs/narr/MyProject/myproject/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/narr/MyProject/production.ini b/docs/narr/MyProject/production.ini
index c1d0eee82..7a5674d23 100644
--- a/docs/narr/MyProject/production.ini
+++ b/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
diff --git a/docs/narr/MyProject/setup.py b/docs/narr/MyProject/setup.py
index a64d65ba6..a0b6fab9e 100644
--- a/docs/narr/MyProject/setup.py
+++ b/docs/narr/MyProject/setup.py
@@ -6,7 +6,7 @@ here = os.path.abspath(os.path.dirname(__file__))
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',
diff --git a/docs/narr/advconfig.rst b/docs/narr/advconfig.rst
index 3bd9c2a4e..8040a465f 100644
--- a/docs/narr/advconfig.rst
+++ b/docs/narr/advconfig.rst
@@ -14,7 +14,7 @@ you to ignore relative configuration statement ordering in some
circumstances.
.. index::
- single: imperative configuration
+ pair: configuration; conflict detection
.. _conflict_detection:
@@ -299,6 +299,9 @@ These are the methods of the configurator which provide conflict detection:
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: the routes they imply require relative ordering. Such ordering
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``
diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst
index 0d50b0106..e609a3eab 100644
--- a/docs/narr/assets.rst
+++ b/docs/narr/assets.rst
@@ -35,6 +35,9 @@ static assets. For example, there's a ``static`` directory which contains
``.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 @@ individual documentation.
.. index::
single: add_static_view
+ pair: assets; serving
.. _static_assets_section:
@@ -186,6 +190,7 @@ discussed in more detail later in this chapter.
.. index::
single: generating static asset urls
single: static asset urls
+ pair: assets; generating urls
.. _generating_static_asset_urls:
@@ -299,7 +304,7 @@ URLs against assets made accessible by registering a custom static view.
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 @@ its behavior is almost exactly the same once it's configured.
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 @@ application's startup code.
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 @@ feature, a :term:`Configurator` API exists named
- 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.
diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst
new file mode 100644
index 000000000..0f23c153c
--- /dev/null
+++ b/docs/narr/commandline.rst
@@ -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')
diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst
index 6360dc574..597d48b09 100644
--- a/docs/narr/configuration.rst
+++ b/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 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.
+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.
+
+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 @@ itself. When you plug code into the :app:`Pyramid` framework, you are
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 @@ including conditionals, can be employed in this mode of configuration.
.. _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 @@ the scanner, a set of calls are made to a :term:`Configurator` on your
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 @@ In the example above, the scanner translates the arguments to
config.add_view(hello)
-Declarative Configuration
--------------------------
-
-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.
+Summary
+-------
+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.
diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst
index a57b316e1..6465c2a1e 100644
--- a/docs/narr/environment.rst
+++ b/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 @@ changes to templates take effect immediately during development. This
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 @@ also :ref:`overriding_assets_section`.
+---------------------------------+-----------------------------+
| Environment Variable Name | Config File Setting Name |
+=================================+=============================+
-| ``PYRAMID_RELOAD_ASSETS`` | ``reload_assets`` |
+| ``PYRAMID_RELOAD_ASSETS`` | ``pyramid.reload_assets`` |
| | |
| | |
| | |
@@ -70,7 +73,7 @@ also :ref:`overriding_assets_section`.
.. 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 @@ Debugging Authorization
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 @@ Debugging Not Found Errors
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 @@ Debugging Route Matching
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 @@ Turns on all ``debug*`` settings.
+---------------------------------+-----------------------------+
| Environment Variable Name | Config File Setting Name |
+=================================+=============================+
-| ``PYRAMID_DEBUG_ALL`` | ``debug_all`` |
+| ``PYRAMID_DEBUG_ALL`` | ``pyramid.debug_all`` |
| | |
| | |
| | |
@@ -139,7 +161,7 @@ Turns on all ``reload*`` settings.
+---------------------------------+-----------------------------+
| Environment Variable Name | Config File Setting Name |
+=================================+=============================+
-| ``PYRAMID_RELOAD_ALL`` | ``reload_all`` |
+| ``PYRAMID_RELOAD_ALL`` | ``pyramid.reload_all`` |
| | |
| | |
| | |
@@ -154,14 +176,14 @@ The value supplied here is used as the default locale name when a
: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 @@ an example of such a section:
[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 @@ respective settings in the ``[app:main]`` section of your
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 @@ affect settings that do not start with ``reload_*`` such as
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:
diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst
index f5adad905..66632e123 100644
--- a/docs/narr/firstapp.rst
+++ b/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 @@ more detail how it works.
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 @@ defined imports and function definitions, placed within the confines of an
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 @@ The ``config = Configurator()`` line above creates an instance of the
: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 @@ see :class:`~pyramid.config.Configurator` .
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`.
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 94701c9f9..f7ee82821 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -61,8 +61,8 @@ Here's some sample code that implements a minimal NotFound view callable:
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 @@ Here's some sample code that implements a minimal forbidden view:
``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 @@ Another (deprecated) mechanism which allows event subscribers more control
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 @@ Using Response Callbacks
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 @@ The default context URL generator is available for perusal as the class
.. index::
single: IResponse
+ single: special view responses
.. _using_iresponse:
@@ -536,7 +537,8 @@ Changing How Pyramid Treats View Responses
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 @@ to make sure the object implements every attribute and method outlined in
: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
@@ -620,6 +625,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 @@ performed, enabling you to set up the utility in advance:
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`.
diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst
index 97adaeafd..ab1bf20bb 100644
--- a/docs/narr/hybrid.rst
+++ b/docs/narr/hybrid.rst
@@ -79,6 +79,9 @@ Typically, an application that uses traversal exclusively won't perform any
calls to :meth:`pyramid.config.Configurator.add_route` in its startup
code.
+.. index::
+ single: hybrid applications
+
Hybrid Applications
-------------------
@@ -177,6 +180,9 @@ match is straightforward. When a route is matched:
root factory were explained previously within
:ref:`the_resource_tree`.
+.. index::
+ pair: hybrid applications; *traverse route pattern
+
.. _using_traverse_in_a_route_pattern:
Using ``*traverse`` In a Route Pattern
@@ -400,6 +406,9 @@ Traversal will begin at the root object implied by this route (either
the global root, or the object returned by the ``factory`` associated
with this route).
+.. index::
+ pair: hybrid applications; global views
+
Making Global Views Match
+++++++++++++++++++++++++
@@ -420,6 +429,7 @@ attribute.
config.add_view('myproject.views.bazbuz', name='bazbuz')
.. index::
+ pair: hybrid applications; *subpath
single: route subpath
single: subpath (route)
@@ -431,8 +441,9 @@ Using ``*subpath`` in a Route Pattern
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 @@ commonly in route declarations that look like this:
.. code-block:: python
:linenos:
+ from pryamid.static import static_view
+
+ www = static_view('mypackage:static', use_subpath=True)
+
config.add_route('static', '/static/*subpath')
- config.add_view('mypackage.views.static_view', route_name='static')
+ config.add_view(www, route_name='static')
-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.
+``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
------------
diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst
index c21a19b5b..ba5490b31 100644
--- a/docs/narr/i18n.rst
+++ b/docs/narr/i18n.rst
@@ -353,6 +353,9 @@ The message catalog ``.pot`` template will end up in:
``myapplication/locale/myapplication.pot``.
+.. index::
+ single: translation domains
+
Translation Domains
+++++++++++++++++++
@@ -494,6 +497,8 @@ translations will be available to :app:`Pyramid`.
.. index::
single: localizer
single: get_localizer
+ single: translation
+ single: pluralization
Using a Localizer
-----------------
@@ -618,7 +623,7 @@ You can obtain the locale name related to a request by using the
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 @@ this support out of the box and may need special code to do an
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 @@ Internationalization" which explains how to add idiomatic I18N support to
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 @@ time:
: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 @@ application's Paster ``.ini`` file:
[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 @@ If this setting is supplied within the :app:`Pyramid` application
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 @@ languages" configuration scheme as necessary.
pair: locale; negotiator
single: translation directory
+.. index::
+ pair: activating; translation
+
.. _activating_translation:
Activating Translation
@@ -869,6 +883,9 @@ To turn translation on, you must:
- ensure that your application sets the :term:`locale name` correctly.
+.. index::
+ pair: translation directory; adding
+
.. _adding_a_translation_directory:
Adding a Translation Directory
@@ -906,6 +923,9 @@ will be merged into translations from a message catalog added earlier
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 @@ things before any translations need to be performed:
function into that application as required. See
:ref:`custom_locale_negotiator`.
+.. index::
+ single: locale negotiator
+
.. _locale_negotiators:
Locale Negotiators
diff --git a/docs/narr/install.rst b/docs/narr/install.rst
index fe8459c6f..f543753ce 100644
--- a/docs/narr/install.rst
+++ b/docs/narr/install.rst
@@ -9,21 +9,19 @@ Installing :app:`Pyramid`
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
@@ -37,6 +35,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
++++++++++++++++++++++
@@ -55,6 +56,9 @@ command:
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 @@ manager. For example, this works to do so on an Ubuntu Linux system:
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
@@ -98,6 +102,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 @@ setuptools`` within the Python interpreter you'd like to run
.. 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 @@ the script. To remediate this, you may need to do:
$ sudo python ez_setup.py
.. index::
- single: virtualenv
+ pair: install; virtualenv
Installing the ``virtualenv`` Package
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -207,6 +214,7 @@ to install it as your system's administrative user. For example:
.. index::
single: virtualenv
+ pair: Python; virtual environment
Creating the Virtual Python Environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -315,6 +323,9 @@ Installing :app:`Pyramid` on Google App Engine
: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
--------------------------------------
diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst
index a0b682e25..63c31d340 100644
--- a/docs/narr/introduction.rst
+++ b/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 @@ applications.
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 @@ Openness
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 @@ includes details about how :app:`Pyramid` relates to the Pylons Project.
: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 @@ The concept of :term:`view` is used by :app:`Pyramid` mostly as it would be
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
diff --git a/docs/narr/muchadoabouttraversal.rst b/docs/narr/muchadoabouttraversal.rst
index a4709ef18..6ad33c1ce 100644
--- a/docs/narr/muchadoabouttraversal.rst
+++ b/docs/narr/muchadoabouttraversal.rst
@@ -39,6 +39,9 @@ web developer so you know when you might want to use them. :term:`Traversal`
is actually a straightforward metaphor easily comprehended by anyone who's
ever used a run-of-the-mill file system with folders and files.
+.. index::
+ single: URL dispatch
+
URL Dispatch
------------
@@ -100,12 +103,12 @@ from this process to the client as the final result. The server
configuration specified which files would trigger some dynamic code, with the
default case being to just serve the static file.
+.. index::
+ single: traversal
+
Traversal (aka Resource Location)
---------------------------------
-.. 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
something different based on what type of file a given request specifies,
@@ -141,6 +144,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 @@ system. Traversal is in fact a superset of file system lookup.
.. note:: See the chapter entitled :ref:`resources_chapter` for a more
technical overview of resources.
+.. index::
+ single: view lookup
+
View Lookup
-----------
diff --git a/docs/narr/project-debug.png b/docs/narr/project-debug.png
new file mode 100644
index 000000000..d13a91736
--- /dev/null
+++ b/docs/narr/project-debug.png
Binary files differ
diff --git a/docs/narr/project.png b/docs/narr/project.png
index da5bc870b..fc00ec086 100644
--- a/docs/narr/project.png
+++ b/docs/narr/project.png
Binary files differ
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 631412f42..baf4c86fa 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -12,9 +12,9 @@ A project is a directory that contains at least one Python :term:`package`.
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 @@ We'll choose the ``pyramid_starter`` scaffold for this purpose.
$ 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 @@ command ``python setup.py develop``
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 @@ Elided output from a run of this command is shown below:
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 @@ create`` -generated project. Within a project generated by the
``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 @@ For more detailed information about the startup process, see
configuration file settings that influence startup and runtime behavior, see
:ref:`environment_chapter`.
+.. index::
+ single: mod_wsgi
+ single: WSGI
+
Viewing the Application
-----------------------
@@ -410,6 +309,45 @@ browser like what is displayed in the following image:
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
The code generated by a :app:`Pyramid` scaffold assumes that you
@@ -520,14 +458,13 @@ serve``, as well as the deployment settings provided to that application.
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 @@ the default.
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 default.
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 @@ for each request.
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 @@ implementations.
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 @@ who want to use your application.
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 @@ also informs Python that the directory which contains it is a *package*.
Line 12 returns a :term:`WSGI` application to the caller of the function
(Paste).
+.. index::
+ single: views.py
+
``views.py``
~~~~~~~~~~~~
@@ -876,14 +842,14 @@ file call to ``add_view``).
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 @@ about which sort of data storage you'll want to use, so the sample
application uses an instance of :class:`myproject.resources.Root` to
represent the root.
+.. index::
+ single: static directory
+
``static``
~~~~~~~~~~
@@ -924,7 +893,7 @@ template. It includes CSS and images.
``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
@@ -955,6 +924,9 @@ example.
See :ref:`testing_chapter` for more information about writing :app:`Pyramid`
unit tests.
+.. index::
+ pair: modifying; package structure
+
.. _modifying_package_structure:
Modifying Package Structure
@@ -1024,4 +996,13 @@ This pattern can be used to rearrage code referred to by any Pyramid API
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.
+
+
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index f329a7af9..ed391f4fe 100644
--- a/docs/narr/renderers.rst
+++ b/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 @@ Views which use the JSON renderer can vary non-body response attributes by
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 @@ The above configuration will use the file named ``foo.mak`` in the
``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 @@ people with older code bases.
returning various values in the ``response_headerlist``, this is purely a
convenience.
-.. index::
- single: renderer (adding)
-
.. _adding_and_overriding_renderers:
Adding and Changing Renderers
@@ -550,6 +551,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:
Adding a New Renderer
@@ -676,6 +680,9 @@ ending with ``.jinja2`` in its ``renderer`` value. The ``name`` passed
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 @@ the ``name`` attribute to the renderer tag:
config.add_renderer(None, 'mypackage.json_renderer_factory')
+.. index::
+ pair: renderer; overriding at runtime
+
Overriding A Renderer At Runtime
--------------------------------
diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst
index fa8ccc549..0e0d00020 100644
--- a/docs/narr/resources.rst
+++ b/docs/narr/resources.rst
@@ -286,6 +286,9 @@ The shortcut method of the :term:`request` named
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 @@ qualified, should end in a slash, and should not contain any query string or
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
---------------------------------
@@ -368,6 +374,9 @@ The resource passed in must be :term:`location`-aware.
The presence or absence of a :term:`virtual root` has no impact on the
behavior of :func:`~pyramid.traversal.resource_path`.
+.. index::
+ pair: resource; finding by path
+
Finding a Resource by Path
--------------------------
@@ -404,6 +413,9 @@ tree does not exist), a :exc:`KeyError` will be raised.
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
-----------------------------------
@@ -471,6 +483,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 @@ directly provided by an instance instead of overwriting them like
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
-------------------------------------------------------
diff --git a/docs/narr/router.rst b/docs/narr/router.rst
index 0812f7ec7..d08261b17 100644
--- a/docs/narr/router.rst
+++ b/docs/narr/router.rst
@@ -2,6 +2,7 @@
single: request processing
single: request
single: router
+ single: request lifecycle
.. _router_chapter:
diff --git a/docs/narr/security.rst b/docs/narr/security.rst
index c7a07b857..a61578e21 100644
--- a/docs/narr/security.rst
+++ b/docs/narr/security.rst
@@ -168,6 +168,9 @@ normal application operations, the requesting user will need to possess the
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 @@ When a default permission is registered:
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 a default permission is registered:
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 @@ which ACL permitted or denied the authorization based on
authentication information.
This behavior can also be turned on in the application ``.ini`` file
-by setting the ``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 @@ application's configuration section, e.g.:
[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 @@ that implements the following interface:
.. code-block:: python
:linenos:
- class AuthenticationPolicy(object):
+ class IAuthenticationPolicy(object):
""" An object representing a Pyramid authentication policy. """
def authenticated_userid(self, request):
diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst
index 97e3ebc55..6ff9e3dea 100644
--- a/docs/narr/sessions.rst
+++ b/docs/narr/sessions.rst
@@ -15,6 +15,9 @@ implementations :app:`Pyramid` provides out of the box, how to store and
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
@@ -65,6 +68,9 @@ application by using the ``session_factory`` argument to the
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 @@ Some gotchas:
.. index::
single: pyramid_beaker
single: Beaker
+ single: session factory (alternates)
.. _using_alternate_session_factories:
@@ -153,7 +160,7 @@ based sessions, and encrypted cookie-based sessions. See
``pyramid_beaker``.
.. index::
- single: session factory
+ single: session factory (custom)
Creating Your Own Session Factory
---------------------------------
@@ -184,6 +191,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 @@ The ``allow_duplicate`` argument defaults to ``True``. If this is
``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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -255,6 +268,9 @@ been popped.
>>> request.session.pop_flash()
[]
+.. index::
+ single: session.peek_flash
+
Using the ``session.peek_flash`` Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -288,7 +304,7 @@ Preventing Cross-Site Request Forgery Attacks
`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
@@ -298,6 +314,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 @@ input field named ``csrf_token``:
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst
index e2c43b17e..c9ed01f83 100644
--- a/docs/narr/startup.rst
+++ b/docs/narr/startup.rst
@@ -53,6 +53,10 @@ Here's a high-level time-ordered overview of what happens when you press
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 @@ Here's a high-level time-ordered overview of what happens when you press
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 @@ Here's a high-level time-ordered overview of what happens when you press
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 @@ Here's a high-level time-ordered overview of what happens when you press
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
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index 150b173e3..0f46f6422 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -241,6 +241,9 @@ of :func:`~pyramid.renderers.render` (a string):
single: renderer (template)
+.. index::
+ pair: renderer; system values
+
.. _renderer_system_values:
System Values Used During Rendering
@@ -277,6 +280,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:
Templates Used as Renderers via Configuration
@@ -426,7 +432,7 @@ See also :ref:`built_in_renderers` for more general information about
renderers, including Chameleon ZPT renderers.
.. index::
- single: sample template
+ single: ZPT template (sample)
A Sample ZPT Template
~~~~~~~~~~~~~~~~~~~~~
@@ -448,7 +454,7 @@ Here's what a simple :term:`Chameleon` ZPT template used under
<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 @@ Note that I always name my Chameleon ZPT template files with a ``.pt``
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 @@ variable set to ``1``, For example:
$ 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 @@ displaying the arguments passed to the template itself.
.. 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 @@ in the ``templates`` subdirectory of the ``mypackage`` Python package. See
``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 @@ look like:
<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 @@ variable set to ``1``, For example:
$ 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 @@ application's configuration section, e.g.:
[app:main]
use = egg:MyProject#app
- reload_templates = true
+ pyramid.reload_templates = true
.. index::
single: template system bindings
diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst
index e1715dc25..aa36b4455 100644
--- a/docs/narr/traversal.rst
+++ b/docs/narr/traversal.rst
@@ -456,6 +456,103 @@ as the sole argument: ``request``; it is expected to return a response.
-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
----------
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index f94ed3ba8..61c9770c6 100644
--- a/docs/narr/urldispatch.rst
+++ b/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 @@ If route configuration is present in an application, the :app:`Pyramid`
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 @@ attributes.
.. _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 @@ example:
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.
-
-.. 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:
+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.
- .. code-block:: python
+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``:
- 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.
+.. code-block:: python
- 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`.
+ # in your project's __init__.py (mypackage.__init__)
-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.
+ config.add_route('myroute', '/prefix/{one}/{two}')
+ config.scan('mypackage')
-Here's an example route configuration that references a view callable:
+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
- from myproject.views import myview
- config.add_route('myroute', '/prefix/{one}/{two}', view=myview)
-
-You can also pass a :term:`dotted Python name` as the ``view`` argument
-rather than an actual callable:
-
-.. code-block:: python
- :linenos:
+ # in your project's views.py module (mypackage.views)
- # "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')
+ from pyramid.view import view_config
+ from pyramid.response import Response
-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.
+ @view_config(route_name='myroute')
+ def myview(request):
+ return Response('OK')
-See :meth:`pyramid.config.Configurator.add_route` for a description of
-view-related arguments.
+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 Declaration Ordering
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 @@ a separate :term:`ACL`, as documented in
combine URL dispatch with :term:`traversal` as documented within
:ref:`hybrid_chapter`.
+.. index::
+ single: route configuration arguments
+
Route Configuration Arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -439,13 +376,17 @@ the associated route to be considered a match during the route matching
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:
Custom Route Predicates
@@ -578,32 +519,38 @@ that the year match argument is '2010' if and only if the route name is
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.
-
-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.
+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 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 @@ representing a :term:`SQLAlchemy` model.
single: matching the root URL
single: root url (matching)
+.. index::
+ pair: matching; root URL
+
Matching the Root URL
---------------------
@@ -958,6 +908,9 @@ The ``notfound_view`` supplied must adhere to the two-argument view callable
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 @@ our sample ``Article`` factory class is not very ambitious.
.. 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 @@ Debugging Route Matching
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 @@ which you started the application from. For example:
See :ref:`environment_chapter` for more information about how, and where to
set these values.
-.. 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.
+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`.
-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.
+.. index::
+ pair: route; view callable lookup details
Route View Callable Registration and Lookup Details
---------------------------------------------------
@@ -1139,24 +1062,28 @@ when the route pattern is matched during a request. To do so:
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.
-
-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`.
-
-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`.
+ 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.
+
+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.
+
+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`.
-
diff --git a/docs/narr/vhosting.rst b/docs/narr/vhosting.rst
index d3ff260e3..5679cc2e2 100644
--- a/docs/narr/vhosting.rst
+++ b/docs/narr/vhosting.rst
@@ -14,6 +14,9 @@ URL space that it does not "naturally" inhabit.
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
-----------------------------------------
diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst
index ec42446ff..a1b12ad2a 100644
--- a/docs/narr/viewconfig.rst
+++ b/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:
+:term:`View lookup` is the :app:`Pyramid` subsystem responsible for finding
+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.
-View Lookup and Invocation
---------------------------
+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 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.
-
-: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.
-
-: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 @@ to be invoked.
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 @@ View configuration is performed in one of these ways:
- 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 @@ arguments. View predicate arguments used during view configuration are used
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,12 +83,14 @@ the request, :app:`Pyramid` will return an error to the user's browser,
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 @@ arguments that are supplied, the more specific, and narrower the usage of the
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 @@ configured view.
.. _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 @@ a :app:`Pyramid` application module ``views.py``:
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 @@ configuration stanza:
.. 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 @@ See :ref:`configuration_module` for additional API arguments to the
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 @@ If your view callable is a function, it may be used as a function decorator:
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 @@ against a class as when they are applied against a function. For example:
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 @@ decorator syntactic sugar, if you wish:
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 @@ registration. For example:
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 @@ The decorator can also be used against a method of a class:
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 @@ against the ``amethod`` method could be spelled equivalently as the below:
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 @@ against the ``amethod`` method could be spelled equivalently as the below:
.. _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 @@ to the ``@view_config`` decorator. For example:
# 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.
-
-.. 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:
+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.
- 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 @@ configuration using :meth:`~pyramid.config.Configurator.add_view`:
# 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 @@ per :ref:`protecting_views`.
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
-
- 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.
+Debugging View Configuration
+----------------------------
+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.
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index cbd8fcfb7..a3fd61098 100644
--- a/docs/narr/views.rst
+++ b/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 @@ request made to your application.
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 @@ statements with different ``attr`` values, each pointing at a different
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 @@ implements the :term:`Response` interface is to return a
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`.
+
+.. note::
-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`.
+ 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 @@ various other clients. In :app:`Pyramid`, form submission handling logic is
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 @@ using your own response object, you will need to ensure you do this yourself.
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.
+
diff --git a/docs/narr/webob.rst b/docs/narr/webob.rst
index 0ff8e1de7..106024db3 100644
--- a/docs/narr/webob.rst
+++ b/docs/narr/webob.rst
@@ -22,9 +22,9 @@ of :class:`webob.Request`. The :term:`response` returned from a
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 @@ requests and forming WSGI responses. WebOb is a nice way to represent "raw"
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::
@@ -78,6 +78,10 @@ object:
``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 @@ tuples; all the keys are ordered, and all the values are ordered.
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 @@ More detail about the request object API is available in:
- 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 @@ properties. These are parsed, so you can do things like
``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 @@ anything, though if you subclass :class:`pyramid.response.Response` and set
``default_content_type`` you can override this behavior.
.. index::
- single: response exceptions
+ single: exception responses
Exception Responses
+++++++++++++++++++
@@ -390,5 +461,6 @@ More Details
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.
diff --git a/docs/narr/zca.rst b/docs/narr/zca.rst
index a99fd8b24..96aac6a80 100644
--- a/docs/narr/zca.rst
+++ b/docs/narr/zca.rst
@@ -245,7 +245,6 @@ registry at startup time instead of constructing a new one:
globalreg = getGlobalSiteManager()
config = Configurator(registry=globalreg)
config.setup_registry(settings=settings)
- config.hook_zca()
config.include('some.other.application')
return config.make_wsgi_app()
diff --git a/docs/remake b/docs/remake
new file mode 100755
index 000000000..b236f2976
--- /dev/null
+++ b/docs/remake
@@ -0,0 +1 @@
+make clean html SPHINXBUILD=../env26/bin/sphinx-build
diff --git a/docs/tutorials/modwsgi/index.rst b/docs/tutorials/modwsgi/index.rst
index 6e3e4ce37..485eec169 100644
--- a/docs/tutorials/modwsgi/index.rst
+++ b/docs/tutorials/modwsgi/index.rst
@@ -102,8 +102,7 @@ commands and files.
# 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
diff --git a/docs/tutorials/wiki/src/authorization/development.ini b/docs/tutorials/wiki/src/authorization/development.ini
index 1ba746d0e..8c60f4ea9 100644
--- a/docs/tutorials/wiki/src/authorization/development.ini
+++ b/docs/tutorials/wiki/src/authorization/development.ini
@@ -1,25 +1,22 @@
[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
host = 0.0.0.0
diff --git a/docs/tutorials/wiki/src/authorization/production.ini b/docs/tutorials/wiki/src/authorization/production.ini
index 5c47ade9b..2cfca5f80 100644
--- a/docs/tutorials/wiki/src/authorization/production.ini
+++ b/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 @@ debug = false
;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]
diff --git a/docs/tutorials/wiki/src/authorization/setup.py b/docs/tutorials/wiki/src/authorization/setup.py
index adfa70c9f..f3b404516 100644
--- a/docs/tutorials/wiki/src/authorization/setup.py
+++ b/docs/tutorials/wiki/src/authorization/setup.py
@@ -9,8 +9,8 @@ CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = [
'pyramid',
'repoze.zodbconn',
- 'repoze.tm2>=1.0b1', # default_commit_veto
- 'repoze.retry',
+ 'pyramid_tm',
+ 'pyramid_debugtoolbar',
'ZODB3',
'WebError',
'docutils',
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css b/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
index fd1914d8d..c54499ddd 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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 h2,
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;}
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
index d98420680..14b88d16a 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/tutorials/wiki/src/basiclayout/development.ini b/docs/tutorials/wiki/src/basiclayout/development.ini
index 555010bed..3913f45db 100644
--- a/docs/tutorials/wiki/src/basiclayout/development.ini
+++ b/docs/tutorials/wiki/src/basiclayout/development.ini
@@ -1,25 +1,22 @@
[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
host = 0.0.0.0
diff --git a/docs/tutorials/wiki/src/basiclayout/production.ini b/docs/tutorials/wiki/src/basiclayout/production.ini
index 5c47ade9b..2cfca5f80 100644
--- a/docs/tutorials/wiki/src/basiclayout/production.ini
+++ b/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 @@ debug = false
;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]
diff --git a/docs/tutorials/wiki/src/basiclayout/setup.py b/docs/tutorials/wiki/src/basiclayout/setup.py
index 2d540d65b..31833dc7f 100644
--- a/docs/tutorials/wiki/src/basiclayout/setup.py
+++ b/docs/tutorials/wiki/src/basiclayout/setup.py
@@ -9,8 +9,8 @@ CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = [
'pyramid',
'repoze.zodbconn',
- 'repoze.tm2>=1.0b1', # default_commit_veto
- 'repoze.retry',
+ 'pyramid_tm',
+ 'pyramid_debugtoolbar',
'ZODB3',
'WebError',
]
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
index fd1914d8d..c54499ddd 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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 h2,
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;}
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
index c24daa711..f9f351c97 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/tutorials/wiki/src/models/development.ini b/docs/tutorials/wiki/src/models/development.ini
index 1ba746d0e..8c60f4ea9 100644
--- a/docs/tutorials/wiki/src/models/development.ini
+++ b/docs/tutorials/wiki/src/models/development.ini
@@ -1,25 +1,22 @@
[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
host = 0.0.0.0
diff --git a/docs/tutorials/wiki/src/models/production.ini b/docs/tutorials/wiki/src/models/production.ini
index 5c47ade9b..2cfca5f80 100644
--- a/docs/tutorials/wiki/src/models/production.ini
+++ b/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 @@ debug = false
;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]
diff --git a/docs/tutorials/wiki/src/models/setup.py b/docs/tutorials/wiki/src/models/setup.py
index 2d540d65b..31833dc7f 100644
--- a/docs/tutorials/wiki/src/models/setup.py
+++ b/docs/tutorials/wiki/src/models/setup.py
@@ -9,8 +9,8 @@ CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = [
'pyramid',
'repoze.zodbconn',
- 'repoze.tm2>=1.0b1', # default_commit_veto
- 'repoze.retry',
+ 'pyramid_tm',
+ 'pyramid_debugtoolbar',
'ZODB3',
'WebError',
]
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/pylons.css b/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
index a9f49cc85..7e6ec739d 100644
--- a/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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 h2,
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;}
diff --git a/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
index d98420680..14b88d16a 100644
--- a/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/tutorials/wiki/src/tests/development.ini b/docs/tutorials/wiki/src/tests/development.ini
index 1ba746d0e..8c60f4ea9 100644
--- a/docs/tutorials/wiki/src/tests/development.ini
+++ b/docs/tutorials/wiki/src/tests/development.ini
@@ -1,25 +1,22 @@
[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
host = 0.0.0.0
diff --git a/docs/tutorials/wiki/src/tests/production.ini b/docs/tutorials/wiki/src/tests/production.ini
index 5c47ade9b..2cfca5f80 100644
--- a/docs/tutorials/wiki/src/tests/production.ini
+++ b/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 @@ debug = false
;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]
diff --git a/docs/tutorials/wiki/src/tests/setup.py b/docs/tutorials/wiki/src/tests/setup.py
index d2c8f5697..603bb6bac 100644
--- a/docs/tutorials/wiki/src/tests/setup.py
+++ b/docs/tutorials/wiki/src/tests/setup.py
@@ -9,8 +9,8 @@ CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = [
'pyramid',
'repoze.zodbconn',
- 'repoze.tm2>=1.0b1',
- 'repoze.retry',
+ 'pyramid_tm',
+ 'pyramid_debugtoolbar',
'ZODB3',
'WebError',
'docutils',
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css b/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css
index fd1914d8d..c54499ddd 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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 h2,
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;}
diff --git a/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
index d98420680..14b88d16a 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/tutorials/wiki/src/views/development.ini b/docs/tutorials/wiki/src/views/development.ini
index 555010bed..3913f45db 100644
--- a/docs/tutorials/wiki/src/views/development.ini
+++ b/docs/tutorials/wiki/src/views/development.ini
@@ -1,25 +1,22 @@
[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
host = 0.0.0.0
diff --git a/docs/tutorials/wiki/src/views/production.ini b/docs/tutorials/wiki/src/views/production.ini
index 5c47ade9b..2cfca5f80 100644
--- a/docs/tutorials/wiki/src/views/production.ini
+++ b/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 @@ debug = false
;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]
diff --git a/docs/tutorials/wiki/src/views/setup.py b/docs/tutorials/wiki/src/views/setup.py
index daa5e5eb1..6acc004c0 100644
--- a/docs/tutorials/wiki/src/views/setup.py
+++ b/docs/tutorials/wiki/src/views/setup.py
@@ -9,8 +9,8 @@ CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = [
'pyramid',
'repoze.zodbconn',
- 'repoze.tm2>=1.0b1', # default_commit_veto
- 'repoze.retry',
+ 'pyramid_tm',
+ 'pyramid_debugtoolbar',
'ZODB3',
'WebError',
'docutils',
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/pylons.css b/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
index fd1914d8d..c54499ddd 100644
--- a/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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 h2,
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;}
diff --git a/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
index d98420680..14b88d16a 100644
--- a/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index 76ce4b83f..df5e228fd 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -54,7 +54,7 @@ inside our ``models.py`` file. Add the following statements to your
``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 @@ We'll then change the return value of these views to pass the `resulting
.. code-block:: python
:linenos:
- return dict(page = context,
+ return dict(page = page,
content = content,
logged_in = logged_in,
edit_url = edit_url)
diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst
index 6151e0e25..0fd64fde4 100644
--- a/docs/tutorials/wiki2/basiclayout.rst
+++ b/docs/tutorials/wiki2/basiclayout.rst
@@ -57,7 +57,7 @@ The next step is to construct a :term:`Configurator`:
``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
diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini
index 3b615f635..bd71cdba5 100644
--- a/docs/tutorials/wiki2/src/authorization/development.ini
+++ b/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]
diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini
index 0fdc38811..ed8eadacc 100644
--- a/docs/tutorials/wiki2/src/authorization/production.ini
+++ b/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]
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models.py b/docs/tutorials/wiki2/src/authorization/tutorial/models.py
index 53c6d1122..832545cb1 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models.py
@@ -41,7 +41,7 @@ def initialize_sql(engine):
transaction.commit()
except IntegrityError:
# already created
- pass
+ transaction.abort()
class RootFactory(object):
__acl__ = [ (Allow, Everyone, 'view'),
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
index fd1914d8d..c54499ddd 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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 h2,
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;}
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
index d98420680..14b88d16a 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini
index 3b615f635..bd71cdba5 100644
--- a/docs/tutorials/wiki2/src/basiclayout/development.ini
+++ b/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]
diff --git a/docs/tutorials/wiki2/src/basiclayout/production.ini b/docs/tutorials/wiki2/src/basiclayout/production.ini
index 0fdc38811..ed8eadacc 100644
--- a/docs/tutorials/wiki2/src/basiclayout/production.ini
+++ b/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]
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
index 4fd010c5c..9b687931b 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
@@ -40,4 +40,4 @@ def initialize_sql(engine):
try:
populate()
except IntegrityError:
- pass
+ transaction.abort()
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
index fd1914d8d..c54499ddd 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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 h2,
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;}
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
index d98420680..14b88d16a 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/tutorials/wiki2/src/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini
index 3b615f635..bd71cdba5 100644
--- a/docs/tutorials/wiki2/src/models/development.ini
+++ b/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]
diff --git a/docs/tutorials/wiki2/src/models/production.ini b/docs/tutorials/wiki2/src/models/production.ini
index 0fdc38811..ed8eadacc 100644
--- a/docs/tutorials/wiki2/src/models/production.ini
+++ b/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]
diff --git a/docs/tutorials/wiki2/src/models/tutorial/models.py b/docs/tutorials/wiki2/src/models/tutorial/models.py
index ecc8d567b..30f77a0b9 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/models.py
@@ -39,4 +39,4 @@ def initialize_sql(engine):
transaction.commit()
except IntegrityError:
# already created
- pass
+ transaction.abort()
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
index fd1914d8d..c54499ddd 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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 h2,
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;}
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
index d98420680..14b88d16a 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/tutorials/wiki2/src/tests/development.ini b/docs/tutorials/wiki2/src/tests/development.ini
index 3b615f635..bd71cdba5 100644
--- a/docs/tutorials/wiki2/src/tests/development.ini
+++ b/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]
diff --git a/docs/tutorials/wiki2/src/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini
index 0fdc38811..ed8eadacc 100644
--- a/docs/tutorials/wiki2/src/tests/production.ini
+++ b/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]
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models.py b/docs/tutorials/wiki2/src/tests/tutorial/models.py
index 53c6d1122..832545cb1 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/models.py
@@ -41,7 +41,7 @@ def initialize_sql(engine):
transaction.commit()
except IntegrityError:
# already created
- pass
+ transaction.abort()
class RootFactory(object):
__acl__ = [ (Allow, Everyone, 'view'),
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css
index fd1914d8d..c54499ddd 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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 h2,
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;}
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt
index d98420680..14b88d16a 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/tutorials/wiki2/src/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini
index 3b615f635..bd71cdba5 100644
--- a/docs/tutorials/wiki2/src/views/development.ini
+++ b/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]
diff --git a/docs/tutorials/wiki2/src/views/production.ini b/docs/tutorials/wiki2/src/views/production.ini
index 0fdc38811..ed8eadacc 100644
--- a/docs/tutorials/wiki2/src/views/production.ini
+++ b/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]
diff --git a/docs/tutorials/wiki2/src/views/tutorial/models.py b/docs/tutorials/wiki2/src/views/tutorial/models.py
index 960c14941..30506f67e 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/models.py
@@ -38,4 +38,4 @@ def initialize_sql(engine):
transaction.commit()
except IntegrityError:
# already created
- pass
+ transaction.abort()
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
index fd1914d8d..c54499ddd 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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 h2,
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;}
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
index d98420680..14b88d16a 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
+++ b/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>
diff --git a/docs/whatsnew-1.0.rst b/docs/whatsnew-1.0.rst
index 8602c5105..5cd822b0d 100644
--- a/docs/whatsnew-1.0.rst
+++ b/docs/whatsnew-1.0.rst
@@ -62,8 +62,8 @@ fail if you do nothing to your existing :mod:`repoze.bfg` application.
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
diff --git a/docs/whatsnew-1.1.rst b/docs/whatsnew-1.1.rst
index fdf3b1c74..18d0aa0b1 100644
--- a/docs/whatsnew-1.1.rst
+++ b/docs/whatsnew-1.1.rst
@@ -29,6 +29,12 @@ The major feature additions in Pyramid 1.1 are:
- 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 @@ Default HTTP Exception View
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``.
-
-- 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})``.
+- 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.
+
+- 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.
+
+- 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`.
+
+- 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``).
+
+- 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 @@ Minor Feature Additions
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
---------------------------
@@ -291,6 +390,26 @@ Backwards Incompatibilities
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
rendering unsanitized input from users. To revert this behavior in user's
@@ -360,7 +479,7 @@ Deprecations and Behavior Differences
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 @@ Deprecations and Behavior Differences
- 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 @@ Deprecations and Behavior Differences
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 @@ Dependency Changes
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.
diff --git a/pyramid/compat.py b/pyramid/compat.py
new file mode 100644
index 000000000..7d723715e
--- /dev/null
+++ b/pyramid/compat.py
@@ -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
+
diff --git a/pyramid/compat/__init__.py b/pyramid/compat/__init__.py
deleted file mode 100644
index 096fb3ddf..000000000
--- a/pyramid/compat/__init__.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Some code in this file was lifted wholesale from Django
-# <http://djangoproject.com> (see
-# http://code.djangoproject.com/browser/django/trunk/LICENSE for
-# license text; BSD-like)
-
-# License for code in this file that was taken from Python 2.5.
-
-# 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.
-
-
-try: # pragma: no cover
- from functools import wraps
-except ImportError: #pragma no cover
- # < 2.5
- def curry(_curried_func, *args, **kwargs):
- def _curried(*moreargs, **morekwargs):
- return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
- return _curried
-
- ### Begin from Python 2.5 functools.py ###################################
- # Summary of changes made to the Python 2.5 code below:
- # * swapped ``partial`` for ``curry`` to maintain backwards-compatibility
- # in Django.
- # * Wrapped the ``setattr`` call in ``update_wrapper`` with a try-except
- # block to make it compatible with Python 2.3, which doesn't allow
- # assigning to ``__name__``.
-
- # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software
- # Foundation.
- # All Rights Reserved.
- ##########################################################################
-
- # update_wrapper() and wraps() are tools to help write
- # wrapper functions that can handle naive introspection
-
- WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
- WRAPPER_UPDATES = ('__dict__',)
- def update_wrapper(wrapper,
- wrapped,
- assigned = WRAPPER_ASSIGNMENTS,
- updated = WRAPPER_UPDATES):
- """Update a wrapper function to look like the wrapped function
-
- wrapper is the function to be updated
- wrapped is the original function
- assigned is a tuple naming the attributes assigned directly
- from the wrapped function to the wrapper function (defaults to
- functools.WRAPPER_ASSIGNMENTS)
- updated is a tuple naming the attributes off the wrapper that
- are updated with the corresponding attribute from the wrapped
- function (defaults to functools.WRAPPER_UPDATES)
- """
- for attr in assigned:
- setattr(wrapper, attr, getattr(wrapped, attr))
-
- for attr in updated:
- getattr(wrapper, attr).update(getattr(wrapped, attr))
- # Return the wrapper so this can be used as a decorator via curry()
- return wrapper
-
- def wraps(wrapped,
- assigned = WRAPPER_ASSIGNMENTS,
- updated = WRAPPER_UPDATES):
- """Decorator factory to apply update_wrapper() to a wrapper function
-
- Returns a decorator that invokes update_wrapper() with the decorated
- function as the wrapper argument and the arguments to wraps() as the
- remaining arguments. Default arguments are as for update_wrapper().
- This is a convenience function to simplify applying curry() to
- update_wrapper().
- """
- return curry(update_wrapper, wrapped=wrapped,
- assigned=assigned, updated=updated)
-
-### End from Python 2.5 functools.py ##########################################
-
-try:
- all = all
-except NameError: # pragma: no cover
- def all(iterable):
- for element in iterable:
- if not element:
- return False
- return True
-
-try:
- import json
-except ImportError: # pragma: no cover
- try:
- import simplejson as json
- except NotImplementedError:
- from django.utils import simplejson as json # GAE
-
-
-
-try:
- from hashlib import md5
-except ImportError: # pragma: no cover
- import md5
- md5 = md5.new
-
-try:
- any = any # make importable
-except NameError: # pragma: no cover
- def any(L):
- for thing in L:
- if thing:
- return True
- return False
-
diff --git a/pyramid/config.py b/pyramid/config.py
index bad12fc20..3e91fb37a 100644
--- a/pyramid/config.py
+++ b/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 IRendererFactory
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.interfaces import IViewMapperFactory
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 default_exceptionresponse_view
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.renderers import RendererHelper
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.threadlocal import manager
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 @@ class Configurator(object):
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 @@ class Configurator(object):
: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 @@ class Configurator(object):
: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 @@ class Configurator(object):
"""
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 @@ class Configurator(object):
""" 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 @@ class Configurator(object):
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 @@ class Configurator(object):
# 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 @@ class Configurator(object):
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,11 +857,14 @@ class Configurator(object):
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
callables for functions, instances, or classes which meet the
@@ -907,6 +933,123 @@ class Configurator(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
implied by the supplied ``iface`` interface. The
@@ -1003,11 +1146,14 @@ class Configurator(object):
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 @@ class Configurator(object):
self.registry.notify(ApplicationCreated(app))
finally:
self.manager.pop()
+
return app
@action_method
@@ -1057,10 +1204,10 @@ class Configurator(object):
``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 @@ class Configurator(object):
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
@@ -1167,6 +1316,12 @@ class Configurator(object):
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
The :term:`view name` of a different :term:`view
@@ -1229,18 +1384,7 @@ class Configurator(object):
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 @@ class Configurator(object):
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 @@ class Configurator(object):
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 @@ class Configurator(object):
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 @@ class Configurator(object):
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 @@ class Configurator(object):
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 @@ class Configurator(object):
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 @@ class Configurator(object):
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 Configurator(object):
: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 @@ class Configurator(object):
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 @@ class Configurator(object):
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 @@ class Configurator(object):
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 @@ class Configurator(object):
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 @@ class MultiView(object):
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__
- try:
- wrapped_view.__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
+
+ wrapper.__wraps__ = view
+ wrapper.__original_view__ = original_view
+ wrapper.__module__ = view.__module__
+ wrapper.__doc__ = view.__doc__
+
try:
- wrapped_view.__order__ = view.__order__
+ wrapper.__name__ = view.__name__
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 @@ class ViewDeriver(object):
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,12 +3140,16 @@ class ViewDeriver(object):
@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:
seconds, options = seconds
@@ -3016,9 +3160,10 @@ class ViewDeriver(object):
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 @@ class ViewDeriver(object):
@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 @@ class ViewDeriver(object):
@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
-
- def _rendered_view(context, request):
- renderer = static_renderer
- result = wrapped_view(context, request)
- registry = self.kw['registry']
+ return self._rendered_view(view, renderer)
+
+ 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 @@ class ViewDeriver(object):
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 @@ def isexception(o):
(inspect.isclass(o) and (issubclass(o, Exception)))
)
+global_registries = WeakOrderedSet()
+
diff --git a/pyramid/events.py b/pyramid/events.py
index 22cbf0cb2..4021b94cc 100644
--- a/pyramid/events.py
+++ b/pyramid/events.py
@@ -179,35 +179,48 @@ class BeforeRender(dict):
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).
-
- 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. """
-
- def __init__(self, system):
+ 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 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.
+
+ 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 @@ class BeforeRender(dict):
""" Return the value for key ``k`` from the renderer globals
dictionary, or the default if no such value exists."""
return self._system.get(k)
-
+
diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py
index 44b854929..4d23db8d2 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -236,6 +236,11 @@ ${body}''')
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)
diff --git a/pyramid/i18n.py b/pyramid/i18n.py
index 7bc096bb1..4b34534af 100644
--- a/pyramid/i18n.py
+++ b/pyramid/i18n.py
@@ -214,6 +214,11 @@ class Translations(gettext.GNUTranslations, object):
: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
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index fee8d549d..7666eb020 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -284,13 +284,18 @@ class IBeforeRender(Interface):
"""
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
@@ -304,6 +309,10 @@ class IBeforeRender(Interface):
""" 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):
""" Call a the renderer implementation with the result of the
@@ -437,6 +446,17 @@ class IMultiDict(Interface): # docs-only interface
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
class IRouteRequest(Interface):
@@ -604,7 +624,7 @@ class IRoute(Interface):
'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 @@ class ISession(Interface):
""" 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 @@ class ISession(Interface):
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 @@ class IRendererInfo(Interface):
settings = Attribute('The deployment settings dictionary related '
'to the current application')
-
diff --git a/pyramid/log.py b/pyramid/log.py
deleted file mode 100644
index 8a29ca919..000000000
--- a/pyramid/log.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import logging
-
-def make_stream_logger(
- name, stream, levelname='DEBUG', fmt='%(asctime)s %(message)s'):
- """ Return an object which implements
- ``pyramid.interfaces.IDebugLogger`` (ie. a Python PEP 282 logger
- instance) with the name ``name`` using the stream (or open
- filehandle) ``stream``, logging at ``levelname`` log level or
- above with format ``fmt``. """
- handler = logging.StreamHandler(stream)
- formatter = logging.Formatter(fmt)
- handler.setFormatter(formatter)
- logger = logging.Logger(name)
- logger.addHandler(handler)
- logger.setLevel(getattr(logging, levelname))
- return logger
diff --git a/pyramid/paster.py b/pyramid/paster.py
index b1f0a6c8b..47f9ebc34 100644
--- a/pyramid/paster.py
+++ b/pyramid/paster.py
@@ -1,3 +1,4 @@
+import ConfigParser
import os
import sys
from code import interact
@@ -7,7 +8,14 @@ import zope.deprecation
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 @@ zope.deprecation.deprecated(
'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 PCommand(Command):
class PShellCommand(PCommand):
"""Open an interactive shell with a :app:`Pyramid` app loaded.
- This command accepts two positional arguments:
-
- ``config_file`` -- specifies the PasteDeploy config file to use
- for the interactive shell.
+ This command accepts one positional argument:
- ``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 @@ class PShellCommand(PCommand):
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
+ ConfigParser = ConfigParser.ConfigParser # testing
+
+ 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)
- 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}
-
- if (IPShell is None) or self.options.disable_ipython:
+ 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 @@ class PRoutesCommand(PCommand):
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:
-
- ``config_file`` -- specifies the PasteDeploy config file to use
- for the interactive shell.
+ This command accepts one positional argument:
- ``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 @@ class PRoutesCommand(PCommand):
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 @@ class PRoutesCommand(PCommand):
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:
-
- ``config_file`` -- specifies the PasteDeploy config file to use
- for the interactive shell.
+ This command accepts two positional arguments:
- ``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 @@ class PViewsCommand(PCommand):
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 @@ class PViewsCommand(PCommand):
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)
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index b201d32c2..6f9b87698 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -3,6 +3,7 @@ import pkg_resources
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 @@ def render_to_response(renderer_name, value, request=None, package=None):
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,17 +338,24 @@ def template_renderer_factory(info, impl, lock=registry_lock):
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)
def __init__(self, name=None, package=None, registry=None):
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 @@ class RendererHelper(object):
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 @@ class RendererHelper(object):
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 @@ class RendererHelper(object):
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()
diff --git a/pyramid/request.py b/pyramid/request.py
index a3848461f..2a654d218 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -204,6 +204,7 @@ class Request(BaseRequest, DeprecatedRequestMethods):
response_callbacks = ()
finished_callbacks = ()
exception = None
+ exc_info = None
matchdict = None
matched_route = None
@@ -491,16 +492,24 @@ class Request(BaseRequest, DeprecatedRequestMethods):
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):
diff --git a/pyramid/response.py b/pyramid/response.py
index 68496e386..db53de0c3 100644
--- a/pyramid/response.py
+++ b/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 @@ from pyramid.interfaces import IResponse
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
diff --git a/pyramid/router.py b/pyramid/router.py
index 458237a8c..248ab3b5b 100644
--- a/pyramid/router.py
+++ b/pyramid/router.py
@@ -2,7 +2,6 @@ from zope.interface import implements
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 IRoutesMapper
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.request import Request
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,14 +37,123 @@ class Router(object):
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):
"""
Accept ``environ`` and ``start_response``; create a
@@ -54,145 +163,28 @@ class Router(object):
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()
diff --git a/pyramid/scaffolds/alchemy/+package+/models.py b/pyramid/scaffolds/alchemy/+package+/models.py
index f1b47f98c..4a93ecf8a 100755
--- a/pyramid/scaffolds/alchemy/+package+/models.py
+++ b/pyramid/scaffolds/alchemy/+package+/models.py
@@ -76,7 +76,7 @@ def initialize_sql(engine):
try:
populate()
except IntegrityError:
- DBSession.rollback()
+ transaction.abort()
return DBSession
def appmaker(engine):
diff --git a/pyramid/scaffolds/alchemy/+package+/static/pylons.css b/pyramid/scaffolds/alchemy/+package+/static/pylons.css
index d952fb42e..c54499ddd 100644
--- a/pyramid/scaffolds/alchemy/+package+/static/pylons.css
+++ b/pyramid/scaffolds/alchemy/+package+/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
diff --git a/pyramid/scaffolds/alchemy/+package+/templates/model.pt_tmpl b/pyramid/scaffolds/alchemy/+package+/templates/model.pt_tmpl
index 9302326f5..83ddd768e 100644
--- a/pyramid/scaffolds/alchemy/+package+/templates/model.pt_tmpl
+++ b/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>
diff --git a/pyramid/scaffolds/alchemy/+package+/templates/root.pt_tmpl b/pyramid/scaffolds/alchemy/+package+/templates/root.pt_tmpl
index b0ed476cc..fc41ce20a 100644
--- a/pyramid/scaffolds/alchemy/+package+/templates/root.pt_tmpl
+++ b/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>
diff --git a/pyramid/scaffolds/alchemy/development.ini_tmpl b/pyramid/scaffolds/alchemy/development.ini_tmpl
index accec1718..4dc4c2461 100644
--- a/pyramid/scaffolds/alchemy/development.ini_tmpl
+++ b/pyramid/scaffolds/alchemy/development.ini_tmpl
@@ -1,23 +1,21 @@
[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
host = 0.0.0.0
diff --git a/pyramid/scaffolds/alchemy/production.ini_tmpl b/pyramid/scaffolds/alchemy/production.ini_tmpl
index 0deb1dd5f..dadf6c366 100644
--- a/pyramid/scaffolds/alchemy/production.ini_tmpl
+++ b/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 @@ debug = false
;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]
diff --git a/pyramid/scaffolds/alchemy/setup.py_tmpl b/pyramid/scaffolds/alchemy/setup.py_tmpl
index 8e9da9755..183d8cc24 100644
--- a/pyramid/scaffolds/alchemy/setup.py_tmpl
+++ b/pyramid/scaffolds/alchemy/setup.py_tmpl
@@ -9,7 +9,8 @@ CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = [
'pyramid',
- 'repoze.tm2>=1.0b1', # default_commit_veto
+ 'pyramid_tm',
+ 'pyramid_debugtoolbar',
'sqlalchemy',
'zope.sqlalchemy',
'WebError',
diff --git a/pyramid/scaffolds/routesalchemy/+package+/models.py b/pyramid/scaffolds/routesalchemy/+package+/models.py
index a036e8c91..e942732ec 100644
--- a/pyramid/scaffolds/routesalchemy/+package+/models.py
+++ b/pyramid/scaffolds/routesalchemy/+package+/models.py
@@ -39,4 +39,4 @@ def initialize_sql(engine):
try:
populate()
except IntegrityError:
- DBSession.rollback()
+ transaction.abort()
diff --git a/pyramid/scaffolds/routesalchemy/+package+/static/pylons.css b/pyramid/scaffolds/routesalchemy/+package+/static/pylons.css
index d952fb42e..c54499ddd 100644
--- a/pyramid/scaffolds/routesalchemy/+package+/static/pylons.css
+++ b/pyramid/scaffolds/routesalchemy/+package+/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
diff --git a/pyramid/scaffolds/routesalchemy/+package+/templates/mytemplate.pt_tmpl b/pyramid/scaffolds/routesalchemy/+package+/templates/mytemplate.pt_tmpl
index 4fbc554e9..3cd9c66a4 100644
--- a/pyramid/scaffolds/routesalchemy/+package+/templates/mytemplate.pt_tmpl
+++ b/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>
diff --git a/pyramid/scaffolds/routesalchemy/development.ini_tmpl b/pyramid/scaffolds/routesalchemy/development.ini_tmpl
index accec1718..4dc4c2461 100644
--- a/pyramid/scaffolds/routesalchemy/development.ini_tmpl
+++ b/pyramid/scaffolds/routesalchemy/development.ini_tmpl
@@ -1,23 +1,21 @@
[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
host = 0.0.0.0
diff --git a/pyramid/scaffolds/routesalchemy/production.ini_tmpl b/pyramid/scaffolds/routesalchemy/production.ini_tmpl
index 0deb1dd5f..9fd182ba3 100644
--- a/pyramid/scaffolds/routesalchemy/production.ini_tmpl
+++ b/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 @@ debug = false
;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]
diff --git a/pyramid/scaffolds/routesalchemy/setup.py_tmpl b/pyramid/scaffolds/routesalchemy/setup.py_tmpl
index 936a5dde0..cd5eacfb7 100644
--- a/pyramid/scaffolds/routesalchemy/setup.py_tmpl
+++ b/pyramid/scaffolds/routesalchemy/setup.py_tmpl
@@ -11,7 +11,8 @@ requires = [
'pyramid',
'SQLAlchemy',
'transaction',
- 'repoze.tm2>=1.0b1', # default_commit_veto
+ 'pyramid_tm',
+ 'pyramid_debugtoolbar',
'zope.sqlalchemy',
'WebError',
]
diff --git a/pyramid/scaffolds/starter/+package+/static/pylons.css b/pyramid/scaffolds/starter/+package+/static/pylons.css
index d952fb42e..c54499ddd 100644
--- a/pyramid/scaffolds/starter/+package+/static/pylons.css
+++ b/pyramid/scaffolds/starter/+package+/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
diff --git a/pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl b/pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl
index 4fbc554e9..3cd9c66a4 100644
--- a/pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl
+++ b/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>
diff --git a/pyramid/scaffolds/starter/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl
index 60e52da57..bbb448055 100644
--- a/pyramid/scaffolds/starter/development.ini_tmpl
+++ b/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]
diff --git a/pyramid/scaffolds/starter/production.ini_tmpl b/pyramid/scaffolds/starter/production.ini_tmpl
index dea0ef2c6..4ddb3f954 100644
--- a/pyramid/scaffolds/starter/production.ini_tmpl
+++ b/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
diff --git a/pyramid/scaffolds/starter/setup.py_tmpl b/pyramid/scaffolds/starter/setup.py_tmpl
index bd2642627..84a20bd87 100644
--- a/pyramid/scaffolds/starter/setup.py_tmpl
+++ b/pyramid/scaffolds/starter/setup.py_tmpl
@@ -6,7 +6,7 @@ here = os.path.abspath(os.path.dirname(__file__))
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',
diff --git a/pyramid/scaffolds/tests.py b/pyramid/scaffolds/tests.py
index e36943465..d5fcba67c 100644
--- a/pyramid/scaffolds/tests.py
+++ b/pyramid/scaffolds/tests.py
@@ -30,7 +30,6 @@ class TemplateTest(object):
[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 @@ class TemplateTest(object):
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)
diff --git a/pyramid/scaffolds/zodb/+package+/static/pylons.css b/pyramid/scaffolds/zodb/+package+/static/pylons.css
index d952fb42e..c54499ddd 100644
--- a/pyramid/scaffolds/zodb/+package+/static/pylons.css
+++ b/pyramid/scaffolds/zodb/+package+/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
diff --git a/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl b/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl
index 4fbc554e9..3cd9c66a4 100644
--- a/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt_tmpl
+++ b/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>
diff --git a/pyramid/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl
index ae9584690..0ff1a8fb8 100644
--- a/pyramid/scaffolds/zodb/development.ini_tmpl
+++ b/pyramid/scaffolds/zodb/development.ini_tmpl
@@ -1,25 +1,23 @@
[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
host = 0.0.0.0
diff --git a/pyramid/scaffolds/zodb/production.ini_tmpl b/pyramid/scaffolds/zodb/production.ini_tmpl
index ff52a0585..76a406eb1 100644
--- a/pyramid/scaffolds/zodb/production.ini_tmpl
+++ b/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 @@ debug = false
;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]
diff --git a/pyramid/scaffolds/zodb/setup.py_tmpl b/pyramid/scaffolds/zodb/setup.py_tmpl
index 46759b514..87febd524 100644
--- a/pyramid/scaffolds/zodb/setup.py_tmpl
+++ b/pyramid/scaffolds/zodb/setup.py_tmpl
@@ -9,8 +9,8 @@ CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = [
'pyramid',
'repoze.zodbconn',
- 'repoze.tm2>=1.0b1', # default_commit_veto
- 'repoze.retry',
+ 'pyramid_tm',
+ 'pyramid_debugtoolbar',
'ZODB3',
'WebError',
]
diff --git a/pyramid/scripting.py b/pyramid/scripting.py
index a3ec9bee5..47178c22e 100644
--- a/pyramid/scripting.py
+++ b/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 @@ def get_root(app, request=None):
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
diff --git a/pyramid/security.py b/pyramid/security.py
index 6cf63b0b3..a001f7073 100644
--- a/pyramid/security.py
+++ b/pyramid/security.py
@@ -24,6 +24,8 @@ class AllPermissionsList(object):
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
diff --git a/pyramid/settings.py b/pyramid/settings.py
index edea9ce99..7540cb6d6 100644
--- a/pyramid/settings.py
+++ b/pyramid/settings.py
@@ -20,35 +20,57 @@ class Settings(dict):
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 @@ class Settings(dict):
'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)
diff --git a/pyramid/static.py b/pyramid/static.py
index ec7b4cb00..b1fab066f 100644
--- a/pyramid/static.py
+++ b/pyramid/static.py
@@ -14,6 +14,7 @@ from pyramid.asset import resolve_asset_spec
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 @@ class StaticURLInfo(object):
# 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 @@ class StaticURLInfo(object):
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 @@ class static_view(object):
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 @@ class static_view(object):
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 @@ class static_view(object):
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)
diff --git a/pyramid/tests/defpermbugapp/__init__.py b/pyramid/tests/defpermbugapp/__init__.py
index 957bd08f6..49602749c 100644
--- a/pyramid/tests/defpermbugapp/__init__.py
+++ b/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 x_view(request): # pragma: no cover
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')
diff --git a/pyramid/tests/grokkedapp/__init__.py b/pyramid/tests/grokkedapp/__init__.py
index 1411e4c49..562413a41 100644
--- a/pyramid/tests/grokkedapp/__init__.py
+++ b/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 @@ class stacked_class(object):
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 @@ class oldstyle_grokked_class:
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 @@ class grokked_class(object):
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 @@ class MethodViews(Base):
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'
diff --git a/pyramid/tests/grokkedapp/another.py b/pyramid/tests/grokkedapp/another.py
index 48fe81798..529821b5c 100644
--- a/pyramid/tests/grokkedapp/another.py
+++ b/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 @@ class stacked_class(object):
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 @@ class oldstyle_grokked_class:
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 @@ class grokked_class(object):
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
diff --git a/pyramid/tests/grokkedapp/pod/notinit.py b/pyramid/tests/grokkedapp/pod/notinit.py
index 3d01f92d5..91dcd161b 100644
--- a/pyramid/tests/grokkedapp/pod/notinit.py
+++ b/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'
diff --git a/pyramid/tests/grokkedapp/subpackage/__init__.py b/pyramid/tests/grokkedapp/subpackage/__init__.py
index 3e332913a..9e0ddacbd 100644
--- a/pyramid/tests/grokkedapp/subpackage/__init__.py
+++ b/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'
diff --git a/pyramid/tests/grokkedapp/subpackage/notinit.py b/pyramid/tests/grokkedapp/subpackage/notinit.py
index 41f0c5ea8..f7edd0c68 100644
--- a/pyramid/tests/grokkedapp/subpackage/notinit.py
+++ b/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'
diff --git a/pyramid/tests/grokkedapp/subpackage/subsubpackage/__init__.py b/pyramid/tests/grokkedapp/subpackage/subsubpackage/__init__.py
index ade9644ec..fdda0dffe 100644
--- a/pyramid/tests/grokkedapp/subpackage/subsubpackage/__init__.py
+++ b/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'
diff --git a/pyramid/tests/restbugapp/views.py b/pyramid/tests/restbugapp/views.py
index b94851099..2ace59fa9 100644
--- a/pyramid/tests/restbugapp/views.py
+++ b/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):
diff --git a/pyramid/tests/test_compat.py b/pyramid/tests/test_compat.py
deleted file mode 100644
index 16fc28826..000000000
--- a/pyramid/tests/test_compat.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import unittest
-
-class TestAliases(unittest.TestCase):
- def test_all(self):
- from pyramid.compat import all
- self.assertEqual(all([True, True]), True)
- self.assertEqual(all([False, False]), False)
- self.assertEqual(all([False, True]), False)
-
diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py
index 9a92cd052..5f5bbdc53 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/tests/test_config.py
@@ -10,7 +10,8 @@ except:
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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()
@@ -600,6 +630,136 @@ class ConfiguratorTests(unittest.TestCase):
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
from zope.interface import Interface
@@ -671,6 +831,7 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
(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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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')
@@ -2018,6 +2279,13 @@ class ConfiguratorTests(unittest.TestCase):
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)
factory = object()
@@ -2176,17 +2444,19 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
[('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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class ConfiguratorTests(unittest.TestCase):
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestConfiguratorDeprecatedFeatures(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
"(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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
"(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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
"(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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
"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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 @@ class TestViewDeriver(unittest.TestCase):
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 dummyfactory(request):
""" """
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 @@ class DummyRegistry(object):
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
diff --git a/pyramid/tests/test_events.py b/pyramid/tests/test_events.py
index 09f5f17ab..116a48bb7 100644
--- a/pyramid/tests/test_events.py
+++ b/pyramid/tests/test_events.py
@@ -179,9 +179,9 @@ class TestSubscriber(unittest.TestCase):
[(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,10 +195,20 @@ class TestBeforeRender(unittest.TestCase):
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)
+ result = event.setdefault('a', 1)
+ self.assertEqual(result, 1)
+ self.assertEqual(system, {'a':1})
+
+ def test_setdefault_success(self):
+ system = {}
event = self._makeOne(system)
- self.assertRaises(KeyError, event.__setitem__, 'a', 1)
+ 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}
@@ -206,11 +216,6 @@ class TestBeforeRender(unittest.TestCase):
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}
event = self._makeOne(system)
@@ -241,6 +246,11 @@ class TestBeforeRender(unittest.TestCase):
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):
diff --git a/pyramid/tests/test_httpexceptions.py b/pyramid/tests/test_httpexceptions.py
index 203d442f7..7db071d03 100644
--- a/pyramid/tests/test_httpexceptions.py
+++ b/pyramid/tests/test_httpexceptions.py
@@ -232,6 +232,17 @@ class TestWSGIHTTPException(unittest.TestCase):
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')
diff --git a/pyramid/tests/test_i18n.py b/pyramid/tests/test_i18n.py
index 97117a8cd..fcc41b08e 100644
--- a/pyramid/tests/test_i18n.py
+++ b/pyramid/tests/test_i18n.py
@@ -65,6 +65,19 @@ class TestLocalizer(unittest.TestCase):
)
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 @@ class TestTranslations(unittest.TestCase):
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):
diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py
index dd77d3aec..1ebf83062 100644
--- a/pyramid/tests/test_integration.py
+++ b/pyramid/tests/test_integration.py
@@ -3,7 +3,7 @@ import unittest
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 @@ class WGSIAppPlusViewConfigTests(unittest.TestCase):
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):
diff --git a/pyramid/tests/test_log.py b/pyramid/tests/test_log.py
deleted file mode 100644
index 63add76ef..000000000
--- a/pyramid/tests/test_log.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import unittest
-
-class TestFunctions(unittest.TestCase):
- def test_make_stream_logger(self):
- from pyramid.log import make_stream_logger
- import logging
- import sys
- logger = make_stream_logger('foo', sys.stderr, levelname='DEBUG',
- fmt='%(message)s')
- self.assertEqual(logger.name, 'foo')
- self.assertEqual(logger.handlers[0].stream, sys.stderr)
- self.assertEqual(logger.handlers[0].formatter._fmt, '%(message)s')
- self.assertEqual(logger.level, logging.DEBUG)
-
-
-
diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py
index cf0b38a80..36c3a51be 100644
--- a/pyramid/tests/test_paster.py
+++ b/pyramid/tests/test_paster.py
@@ -5,135 +5,174 @@ class TestPShellCommand(unittest.TestCase):
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)
-
- def test_command_ipshell_is_not_None_ipython_disabled(self):
+ shell = command.make_default_shell(interact)
+ shell({'foo': 'bar'}, 'a help message')
+ self.assertEqual(interact.local, {'foo': 'bar'})
+ self.assertTrue('a help message' in interact.banner)
+
+ def test_make_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)
-
- def test_command_ipython_enabled(self):
+ 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_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)
-
- def test_command_get_app_hookable(self):
- from paste.deploy import loadapp
+ 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_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})])
-
- def test_command_get_root_hookable(self):
+ 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_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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPRoutesCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
(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 @@ class TestPViewsCommand(unittest.TestCase):
(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 @@ class TestPViewsCommand(unittest.TestCase):
(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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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 @@ class TestPViewsCommand(unittest.TestCase):
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')
@@ -779,11 +750,6 @@ class TestPViewsCommand(unittest.TestCase):
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):
from pyramid.paster import get_app
@@ -798,42 +764,165 @@ class TestGetApp(unittest.TestCase):
self.assertEqual(loadapp.section_name, 'myapp')
self.assertEqual(loadapp.relative_to, os.getcwd())
self.assertEqual(result, app)
-
-
-class Dummy:
- pass
+ 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)
-class DummyIPShellFactory(object):
- def __call__(self, argv, user_ns=None):
- shell = DummyIPShell()
- shell(user_ns, {})
- self.shell = shell
- return shell
+ 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 DummyIPShell(object):
- IP = Dummy()
- IP.BANNER = 'foo'
- def __call__(self, local_ns, global_ns):
- self.local_ns = local_ns
- self.global_ns = global_ns
+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 mainloop(self):
- pass
+ 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
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):
self.app = app
@@ -847,22 +936,7 @@ class DummyLoadApp:
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 @@ class DummyMultiView(object):
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,
+ }
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index 18b4caa61..b5c5d1242 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -341,11 +341,15 @@ class TestChameleonRendererLookup(unittest.TestCase):
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)
@@ -482,6 +486,13 @@ class TestRendererHelper(unittest.TestCase):
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):
settings = {'a':1}
@@ -687,6 +698,56 @@ class TestRendererHelper(unittest.TestCase):
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()
diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py
index 0c1c78721..066aa9207 100644
--- a/pyramid/tests/test_request.py
+++ b/pyramid/tests/test_request.py
@@ -233,25 +233,28 @@ class TestRequest(unittest.TestCase):
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 @@ class Test_route_request_iface(unittest.TestCase):
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
diff --git a/pyramid/tests/test_response.py b/pyramid/tests/test_response.py
index 6cb2fd6f4..39360c0af 100644
--- a/pyramid/tests/test_response.py
+++ b/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 @@ class TestResponse(unittest.TestCase):
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')])
diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py
index 5fd2cf01e..2b00cba22 100644
--- a/pyramid/tests/test_router.py
+++ b/pyramid/tests/test_router.py
@@ -4,9 +4,8 @@ from pyramid import testing
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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()
@@ -401,6 +450,33 @@ class TestRouter(unittest.TestCase):
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
from zope.interface import directlyProvides
@@ -713,6 +789,7 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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 @@ class TestRouter(unittest.TestCase):
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()
diff --git a/pyramid/tests/test_scripting.py b/pyramid/tests/test_scripting.py
index d2139b7db..f01e744b8 100644
--- a/pyramid/tests/test_scripting.py
+++ b/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 @@ class TestGetRoot(unittest.TestCase):
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 @@ class TestGetRoot(unittest.TestCase):
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
diff --git a/pyramid/tests/test_settings.py b/pyramid/tests/test_settings.py
index a444539e0..5037959aa 100644
--- a/pyramid/tests/test_settings.py
+++ b/pyramid/tests/test_settings.py
@@ -28,152 +28,337 @@ class TestSettings(unittest.TestCase):
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)
+ 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'})
+ {'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 @@ class TestSettings(unittest.TestCase):
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'})
diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py
index e7506628a..d698ca4f2 100644
--- a/pyramid/tests/test_static.py
+++ b/pyramid/tests/test_static.py
@@ -190,11 +190,12 @@ class Test_static_view(unittest.TestCase):
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 @@ class Test_static_view(unittest.TestCase):
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 @@ class Test_static_view(unittest.TestCase):
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 @@ class Test_static_view(unittest.TestCase):
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 @@ class Test_static_view(unittest.TestCase):
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 @@ class Test_static_view(unittest.TestCase):
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 @@ class Test_static_view(unittest.TestCase):
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 @@ class Test_static_view(unittest.TestCase):
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 @@ class Test_static_view(unittest.TestCase):
'/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 @@ class TestStaticURLInfo(unittest.TestCase):
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 @@ class TestStaticURLInfo(unittest.TestCase):
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)
diff --git a/pyramid/tests/test_tweens.py b/pyramid/tests/test_tweens.py
new file mode 100644
index 000000000..5fa999e8a
--- /dev/null
+++ b/pyramid/tests/test_tweens.py
@@ -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)
+
diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py
index 47aab948a..247b61dad 100644
--- a/pyramid/tests/test_util.py
+++ b/pyramid/tests/test_util.py
@@ -175,3 +175,76 @@ class TestDottedNameResolver(unittest.TestCase):
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
diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py
index ce00454fc..01aa70844 100644
--- a/pyramid/tests/test_view.py
+++ b/pyramid/tests/test_view.py
@@ -121,19 +121,6 @@ class RenderViewToIterableTests(BaseTest, unittest.TestCase):
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 @@ class TestViewConfigDecorator(unittest.TestCase):
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 @@ class Test_patch_mimetypes(unittest.TestCase):
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']
@@ -570,6 +628,18 @@ def make_view(response):
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 @@ class DummyVenusianContext(object):
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
diff --git a/pyramid/tests/venusianapp/__init__.py b/pyramid/tests/venusianapp/__init__.py
new file mode 100644
index 000000000..ce5e07238
--- /dev/null
+++ b/pyramid/tests/venusianapp/__init__.py
@@ -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
+
diff --git a/pyramid/tweens.py b/pyramid/tweens.py
new file mode 100644
index 000000000..5ada88b24
--- /dev/null
+++ b/pyramid/tweens.py
@@ -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'
+
diff --git a/pyramid/url.py b/pyramid/url.py
index 2a6fda89a..548d5e9d9 100644
--- a/pyramid/url.py
+++ b/pyramid/url.py
@@ -404,7 +404,7 @@ def current_route_url(request, *elements, **kw):
``/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``.
diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py
index 230b18e54..3530126eb 100644
--- a/pyramid/urldispatch.py
+++ b/pyramid/urldispatch.py
@@ -5,13 +5,11 @@ from zope.interface import implements
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()
class Route(object):
diff --git a/pyramid/util.py b/pyramid/util.py
index 3e6cd2e60..c0e7640c4 100644
--- a/pyramid/util.py
+++ b/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 @@ class DottedNameResolver(object):
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]()
diff --git a/pyramid/view.py b/pyramid/view.py
index ea20a19c2..1573ee34c 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -4,13 +4,14 @@ import venusian
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,12 +31,27 @@ def init_mimetypes(mimetypes):
# 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
configuration` that matches the :term:`view name` ``name``
@@ -101,11 +117,6 @@ def render_view_to_iterable(context, request, name='', secure=True):
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 @@ class view_config(object):
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)
diff --git a/pyramid/wsgi.py b/pyramid/wsgi.py
index e4c61ff63..3bbe31790 100644
--- a/pyramid/wsgi.py
+++ b/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):
diff --git a/setup.py b/setup.py
index 3b57d6698..5e362ea86 100644
--- a/setup.py
+++ b/setup.py
@@ -53,7 +53,7 @@ if sys.version_info[:2] < (2, 6):
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 @@ setup(name='pyramid',
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
"""
diff --git a/tox.ini b/tox.ini
index e404b29f4..40711a5f2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -26,7 +26,7 @@ deps =
repoze.sphinx.autointerface
virtualenv
nose
- coverage
+ coverage==3.4
nosexcover
# we separate coverage into its own testenv because a) "last run wins" wrt