summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--CHANGES.txt199
-rw-r--r--CONTRIBUTORS.txt6
-rw-r--r--RELEASING.txt6
-rw-r--r--TODO.txt62
-rw-r--r--docs/Makefile8
-rw-r--r--docs/api.rst1
-rw-r--r--docs/api/authentication.rst16
-rw-r--r--docs/api/decorator.rst9
-rw-r--r--docs/api/request.rst56
-rw-r--r--docs/api/session.rst2
-rw-r--r--docs/api/testing.rst2
-rw-r--r--docs/changes.rst2
-rw-r--r--docs/conf.py4
-rw-r--r--docs/designdefense.rst14
-rw-r--r--docs/glossary.rst51
-rw-r--r--docs/index.rst3
-rw-r--r--docs/latexindex.rst3
-rw-r--r--docs/narr/MyProject/myproject/static/pylons.css435
-rw-r--r--docs/narr/advconfig.rst7
-rw-r--r--docs/narr/install.rst213
-rw-r--r--docs/narr/introduction.rst2
-rw-r--r--docs/narr/introspector.rst4
-rw-r--r--docs/narr/project.rst41
-rw-r--r--docs/narr/renderers.rst2
-rw-r--r--docs/narr/sessions.rst13
-rw-r--r--docs/narr/subrequest.rst271
-rw-r--r--docs/narr/templates.rst36
-rw-r--r--docs/narr/testing.rst24
-rw-r--r--docs/narr/upgrading.rst234
-rw-r--r--docs/narr/viewconfig.rst68
-rw-r--r--docs/narr/views.rst10
-rwxr-xr-xdocs/remake2
-rw-r--r--docs/tutorials/modwsgi/index.rst22
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css435
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/views.py10
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css435
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/static/pylons.css435
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/static/pylons.css435
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/views.py10
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/static/pylons.css435
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/views.py10
-rw-r--r--docs/tutorials/wiki2/authorization.rst8
-rw-r--r--docs/tutorials/wiki2/basiclayout.rst28
-rw-r--r--docs/tutorials/wiki2/definingviews.rst2
-rw-r--r--docs/tutorials/wiki2/design.rst3
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/__init__.py6
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css435
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/views.py16
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py6
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css435
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/__init__.py6
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/static/pylons.css435
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/__init__.py6
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css435
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/views.py16
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/__init__.py6
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/static/pylons.css435
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/views.py16
-rw-r--r--docs/whatsnew-1.4.rst310
-rw-r--r--pyramid/__init__.py6
-rw-r--r--pyramid/authentication.py280
-rw-r--r--pyramid/chameleon_text.py18
-rw-r--r--pyramid/chameleon_zpt.py28
-rw-r--r--pyramid/config/factories.py4
-rw-r--r--pyramid/config/predicates.py111
-rw-r--r--pyramid/config/routes.py15
-rw-r--r--pyramid/config/testing.py26
-rw-r--r--pyramid/config/util.py152
-rw-r--r--pyramid/config/views.py71
-rw-r--r--pyramid/decorator.py28
-rw-r--r--pyramid/exceptions.py18
-rw-r--r--pyramid/mako_templating.py11
-rw-r--r--pyramid/renderers.py57
-rw-r--r--pyramid/request.py2
-rw-r--r--pyramid/router.py72
-rw-r--r--pyramid/scaffolds/alchemy/+package+/__init__.py6
-rw-r--r--pyramid/scaffolds/alchemy/+package+/static/pylons.css435
-rw-r--r--pyramid/scaffolds/alchemy/+package+/templates/mytemplate.pt_tmpl16
-rw-r--r--pyramid/scaffolds/alchemy/development.ini_tmpl20
-rw-r--r--pyramid/scaffolds/alchemy/production.ini_tmpl12
-rw-r--r--pyramid/scaffolds/starter/+package+/static/pylons.css435
-rw-r--r--pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl16
-rw-r--r--pyramid/scaffolds/starter/development.ini_tmpl20
-rw-r--r--pyramid/scaffolds/starter/production.ini_tmpl16
-rw-r--r--pyramid/scaffolds/zodb/+package+/static/pylons.css435
-rw-r--r--pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt16
-rw-r--r--pyramid/scaffolds/zodb/development.ini_tmpl20
-rw-r--r--pyramid/scaffolds/zodb/production.ini_tmpl16
-rw-r--r--pyramid/scripting.py4
-rw-r--r--pyramid/scripts/pserve.py8
-rw-r--r--pyramid/security.py14
-rw-r--r--pyramid/session.py24
-rw-r--r--pyramid/testing.py52
-rw-r--r--pyramid/tests/fixtures/withmacro.pt1
-rw-r--r--pyramid/tests/pkgs/subrequestapp/__init__.py50
-rw-r--r--pyramid/tests/test_authentication.py197
-rw-r--r--pyramid/tests/test_chameleon_text.py10
-rw-r--r--pyramid/tests/test_chameleon_zpt.py19
-rw-r--r--pyramid/tests/test_config/test_init.py11
-rw-r--r--pyramid/tests/test_config/test_init.py:TestConfigurator_add_directive0
-rw-r--r--pyramid/tests/test_config/test_predicates.py238
-rw-r--r--pyramid/tests/test_config/test_testing.py24
-rw-r--r--pyramid/tests/test_config/test_tweens.py4
-rw-r--r--pyramid/tests/test_config/test_util.py268
-rw-r--r--pyramid/tests/test_exceptions.py12
-rw-r--r--pyramid/tests/test_integration.py63
-rw-r--r--pyramid/tests/test_mako_templating.py14
-rw-r--r--pyramid/tests/test_renderers.py19
-rw-r--r--pyramid/tests/test_request.py6
-rw-r--r--pyramid/tests/test_scripting.py13
-rw-r--r--pyramid/tests/test_security.py18
-rw-r--r--pyramid/tests/test_session.py25
-rw-r--r--pyramid/tests/test_testing.py55
-rw-r--r--pyramid/tests/test_util.py257
-rw-r--r--pyramid/tweens.py5
-rw-r--r--pyramid/util.py165
-rw-r--r--pyramid/view.py2
-rw-r--r--setup.py3
119 files changed, 8758 insertions, 1824 deletions
diff --git a/.gitignore b/.gitignore
index 8e2f83e7d..5fa2a2ee4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
*.pt.py
*.txt.py
*~
+.*.swp
.coverage
.tox/
nosetests.xml
@@ -21,3 +22,4 @@ bookenv/
jyenv/
pypyenv/
env*/
+venv/
diff --git a/CHANGES.txt b/CHANGES.txt
index 44e594afb..8d5a00e77 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,158 @@
Next release
============
+Features
+--------
+
+- Added an ``effective_principals`` route and view predicate.
+
+- Do not allow the userid returned from the ``authenticated_userid`` or the
+ userid that is one of the list of principals returned by
+ ``effective_principals`` to be either of the strings ``system.Everyone`` or
+ ``system.Authenticated`` when any of the built-in authorization policies that
+ live in ``pyramid.authentication`` are in use. These two strings are
+ reserved for internal usage by Pyramid and they will not be accepted as valid
+ userids.
+
+- Slightly better debug logging from RepozeWho1AuthenticationPolicy.
+
+- ``pyramid.security.view_execution_permitted`` used to return `True` if no
+ view could be found. It now raises a ``TypeError`` exception in that case, as
+ it doesn't make sense to assert that a nonexistent view is
+ execution-permitted. See https://github.com/Pylons/pyramid/issues/299.
+
+- Get rid of shady monkeypatching of ``pyramid.request.Request`` and
+ ``pyramid.response.Response`` done within the ``__init__.py`` of Pyramid.
+ Webob no longer relies on this being done. Instead, the ResponseClass
+ attribute of the Pyramid Request class is assigned to the Pyramid response
+ class; that's enough to satisfy WebOb and behave as it did before with the
+ monkeypatching.
+
+Bug Fixes
+---------
+
+- In the past if a renderer returned ``None``, the body of the resulting
+ response would be set explicitly to the empty string. Instead, now, the body
+ is left unchanged, which allows the renderer to set a body itself by using
+ e.g. ``request.response.body = b'foo'``. The body set by the renderer will
+ be unmolested on the way out. See
+ https://github.com/Pylons/pyramid/issues/709
+
+- In uncommon cases, the ``pyramid_excview_tween_factory`` might have
+ inadvertently raised a ``KeyError`` looking for ``request_iface`` as an
+ attribute of the request. It no longer fails in this case. See
+ https://github.com/Pylons/pyramid/issues/700
+
+Internals
+---------
+
+- Move ``TopologicalSorter`` from ``pyramid.config.util`` to ``pyramid.util``,
+ move ``CyclicDependencyError`` from ``pyramid.config.util`` to
+ ``pyramid.exceptions``, rename ``Singleton`` to ``Sentinel`` and move from
+ ``pyramid.config.util`` to ``pyramid.config.util``; this is in an effort to
+ move that stuff that may be an API one day out of ``pyramid.config.util,
+ because that package should never be imported from non-Pyramid code.
+ TopologicalSorter is still not an API, but may become one.
+
+1.4a3 (2012-10-26)
+==================
+
+Bug Fixes
+---------
+
+- The match_param predicate's text method was fixed to sort its values.
+ Part of https://github.com/Pylons/pyramid/pull/705
+
+- 1.4a ``pyramid.scripting.prepare`` behaved differently than 1.3 series
+ function of same name. In particular, if passed a request, it would not
+ set the ``registry`` attribute of the request like 1.3 did. A symptom
+ would be that passing a request to ``pyramid.paster.bootstrap`` (which uses
+ the function) that did not have a ``registry`` attribute could assume that
+ the registry would be attached to the request by Pyramid. This assumption
+ could be made in 1.3, but not in 1.4. The assumption can now be made in
+ 1.4 too (a registry is attached to a request passed to bootstrap or
+ prepare).
+
+- When registering a view configuration that named a Chameleon ZPT renderer
+ with a macro name in it (e.g. ``renderer='some/template#somemacro.pt``) as
+ well as a view configuration without a macro name it it that pointed to the
+ same template (e.g. ``renderer='some/template.pt'``), internal caching could
+ confuse the two, and your code might have rendered one instead of the
+ other.
+
+Features
+--------
+
+- Allow multiple values to be specified to the ``request_param`` view/route
+ predicate as a sequence. Previously only a single string value was allowed.
+ See https://github.com/Pylons/pyramid/pull/705
+
+- Comments with references to documentation sections placed in scaffold
+ ``.ini`` files.
+
+- Added an HTTP Basic authentication policy
+ at ``pyramid.authentication.BasicAuthAuthenticationPolicy``.
+
+- The Configurator ``testing_securitypolicy`` method now returns the policy
+ object it creates.
+
+- The Configurator ``testing_securitypolicy`` method accepts two new
+ arguments: ``remember_result`` and ``forget_result``. If supplied, these
+ values influence the result of the policy's ``remember`` and ``forget``
+ methods, respectively.
+
+- The DummySecurityPolicy created by ``testing_securitypolicy`` now sets a
+ ``forgotten`` value on the policy (the value ``True``) when its ``forget``
+ method is called.
+
+- The DummySecurityPolicy created by ``testing_securitypolicy`` now sets a
+ ``remembered`` value on the policy, which is the value of the ``principal``
+ argument it's called with when its ``remember`` method is called.
+
+- New ``physical_path`` view predicate. If specified, this value should be a
+ string or a tuple representing the physical traversal path of the context
+ found via traversal for this predicate to match as true. For example:
+ ``physical_path='/'`` or ``physical_path='/a/b/c'`` or ``physical_path=('',
+ 'a', 'b', 'c')``. This is not a path prefix match or a regex, it's a
+ whole-path match. It's useful when you want to always potentially show a
+ view when some object is traversed to, but you can't be sure about what kind
+ of object it will be, so you can't use the ``context`` predicate. The
+ individual path elements inbetween slash characters or in tuple elements
+ should be the Unicode representation of the name of the resource and should
+ not be encoded in any way.
+
+1.4a2 (2012-09-27)
+==================
+
+Bug Fixes
+---------
+
+- When trying to determine Mako defnames and Chameleon macro names in asset
+ specifications, take into account that the filename may have a hyphen in
+ it. See https://github.com/Pylons/pyramid/pull/692
+
+Features
+--------
+
+- A new ``pyramid.session.check_csrf_token`` convenience function was added.
+
+- A ``check_csrf`` view predicate was added. For example, you can now do
+ ``config.add_view(someview, check_csrf=True)``. When the predicate is
+ checked, if the ``csrf_token`` value in ``request.params`` matches the CSRF
+ token in the request's session, the view will be permitted to execute.
+ Otherwise, it will not be permitted to execute.
+
+- Add ``Base.metadata.bind = engine`` to alchemy template, so that tables
+ defined imperatively will work.
+
+Documentation
+-------------
+
+- update wiki2 SQLA tutorial with the changes required after inserting
+ ``Base.metadata.bind = engine`` into the alchemy scaffold.
+
+1.4a1 (2012-09-16)
+==================
Bug Fixes
---------
@@ -33,11 +185,6 @@ Bug Fixes
differ in the case ('text/html' vs. 'text/HTML') will now raise an error.
https://github.com/Pylons/pyramid/pull/620
-- Configurator.add_directive now accepts arbitrary callables like partials or
- objects implementing ``__call__`` which dont have ``__name__`` and
- ``__doc__`` attributes. See https://github.com/Pylons/pyramid/issues/621
- and https://github.com/Pylons/pyramid/pull/647.
-
- Forward-port from 1.3 branch: when registering multiple views with an
``accept`` predicate in a Pyramid application runing under Python 3, you
might have received a ``TypeError: unorderable types: function() <
@@ -46,6 +193,11 @@ Bug Fixes
Features
--------
+- Configurator.add_directive now accepts arbitrary callables like partials or
+ objects implementing ``__call__`` which dont have ``__name__`` and
+ ``__doc__`` attributes. See https://github.com/Pylons/pyramid/issues/621
+ and https://github.com/Pylons/pyramid/pull/647.
+
- Third-party custom view, route, and subscriber predicates can now be added
for use by view authors via
``pyramid.config.Configurator.add_view_predicate``,
@@ -104,13 +256,13 @@ Features
and ``HTTPMovedPermanently`` exceptions, so these can be caught by the
NotFound view (and other exception views).
-- The Mako renderer now accepts a def name in an asset spect. When the def
+- The Mako renderer now supports a def name in an asset spec. When the def
name is present in the asset spec, the system will render the template def
within the template and will return the result. An example asset spec is
``package:path/to/template#defname.mako``. This will render the def named
- ``defname`` inside the ``template.pt`` package instead of rendering the
- entire template. The old way of returning a tuple from the view is
- supported for backward compatibility, ('defname', {}).
+ ``defname`` inside the ``template.mako`` template instead of rendering the
+ entire template. The old way of returning a tuple in the form
+ ``('defname', {})`` from the view is supported for backward compatibility,
- The Chameleon ZPT renderer now accepts a macro name in an asset spec. When
the macro name is present in the asset spec, the system will render the
@@ -151,15 +303,21 @@ Features
``config.add_request_method`` are now available to tweens.
- Request properties and methods added via ``config.set_request_property`` or
- ``config.add_request_method`` are now available to tweens.
-
-- Request properties and methods added via ``config.set_request_property`` or
``config.add_request_method`` are now available in the request object
returned from ``pyramid.paster.bootstrap``.
- ``request.context`` of environment request during ``bootstrap`` is now the
root object if a context isn't already set on a provided request.
+- The ``pyramid.decorator.reify`` function is now an API, and was added to
+ the API documentation.
+
+- Added the ``pyramid.testing.testConfig`` context manager, which can be used
+ to generate a configurator in a test, e.g. ``with testing.testConfig(...):``.
+
+- Users can now invoke a subrequest from within view code using a new
+ ``request.invoke_subrequest`` API.
+
Deprecations
------------
@@ -246,6 +404,23 @@ Backwards Incompatibilities
* ``registerSettings``, use
``pyramid.config.Configurator.add_settings`` instead.
+- In Pyramid 1.3 and previous, the ``__call__`` method of a Response object
+ was invoked before any finished callbacks were executed. As of this
+ release, the ``__call__`` method of a Response object is invoked *after*
+ finished callbacks are executed. This is in support of the
+ ``request.invoke_subrequest`` feature.
+
+Documentation
+-------------
+
+- Added an "Upgrading Pyramid" chapter to the narrative documentation. It
+ describes how to cope with deprecations and removals of Pyramid APIs and
+ how to show Pyramid-generated deprecation warnings while running tests and
+ while running a server.
+
+- Added a "Invoking a Subrequest" chapter to the documentation. It describes
+ how to use the new ``request.invoke_subrequest`` API.
+
Dependencies
------------
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index bb11006f3..d03da3e62 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -1,4 +1,4 @@
- Pylons Project Contributor Agreement
+Pylons Project Contributor Agreement
====================================
The submitter agrees by adding his or her name within the section below named
@@ -177,6 +177,8 @@ Contributors
- Marc Abramowitz, 2012/06/13
+- Brian Sutherland, 2012/06/16
+
- Jeff Cook, 2012/06/16
- Ian Wilson, 2012/06/17
@@ -184,3 +186,5 @@ Contributors
- Roman Kozlovskyi, 2012/08/11
- Domen Kozar, 2012/09/11
+
+- David Gay, 2012/09/16
diff --git a/RELEASING.txt b/RELEASING.txt
index c97c8ef60..379965c53 100644
--- a/RELEASING.txt
+++ b/RELEASING.txt
@@ -13,10 +13,10 @@ Releasing Pyramid
Make sure statement coverage is at 100%::
-- Run Windows tests for Python 2.6, 2.7, and 3.2 if feasible.
+- Run Windows tests for Python 2.6, 2.7, 3.2, and 3.3 if feasible.
-- Make sure all scaffold tests pass (Py 2.6, 2.7, 3.2 and pypy on UNIX; this
- doesn't work on Windows):
+- Make sure all scaffold tests pass (Py 2.6, 2.7, 3.2, 3.3 and pypy on UNIX;
+ this doesn't work on Windows):
$ python pyramid/scaffolds/tests.py
diff --git a/TODO.txt b/TODO.txt
index 627eff7e8..ac5738244 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -6,16 +6,11 @@ Nice-to-Have
- config.set_registry_attr (with conflict detection).
-- _fix_registry should dictify the registry being fixed.
-
- Provide the presumed renderer name to the called view as an attribute of
the request.
- Have action methods return their discriminators.
-- Add docs about upgrading between Pyramid versions (e.g. how to see
- deprecation warnings).
-
- Fix renderers chapter to better document system values passed to template
renderers.
@@ -82,19 +77,49 @@ Nice-to-Have
- Deprecate pyramid.security.view_execution_permitted (it only works for
traversal).
-- Create a function which performs a recursive request.
-
-- Create a ``render_view`` that works by using config.derive_view against an
- existing view callable instead of querying the registry (some sort of API
- for rendering a view callable object to a response from within another view
- callable). Possible idea: have config.add_view mark up the
- function/method/class like @view_config does, then use the attached info to
- derive a view callable whenever called via some API.
-
- Provide a ``has_view`` function.
- Update App engine chapter with less creaky directions.
+- Idea from Zart:
+
+ diff --git a/pyramid/paster.py b/pyramid/paster.py
+ index b0e4d79..b3bd82a 100644
+ --- a/pyramid/paster.py
+ +++ b/pyramid/paster.py
+ @@ -8,6 +8,7 @@ from paste.deploy import (
+ from pyramid.compat import configparser
+ from logging.config import fileConfig
+ from pyramid.scripting import prepare
+ +from pyramid.config import Configurator
+
+ def get_app(config_uri, name=None, loadapp=loadapp):
+ """ Return the WSGI application named ``name`` in the PasteDeploy
+ @@ -111,3 +112,10 @@ def bootstrap(config_uri, request=None):
+ env['app'] = app
+ return env
+
+ +def make_pyramid_app(global_conf, app=None, **settings):
+ + """Return pyramid application configured with provided settings"""
+ + config = Configurator(package='pyramid', settings=settings)
+ + if app:
+ + config.include(app)
+ + app = config.make_wsgi_app()
+ + return app
+ diff --git a/setup.py b/setup.py
+ index 03ebb42..91e0e21 100644
+ --- a/setup.py
+ +++ b/setup.py
+ @@ -118,6 +118,8 @@ setup(name='pyramid',
+ [paste.server_runner]
+ wsgiref = pyramid.scripts.pserve:wsgiref_server_runner
+ cherrypy = pyramid.scripts.pserve:cherrypy_server_runner
+ + [paster.app_factory]
+ + main = pyramid.paster:make_pyramid_app
+ """
+ )
+
+
Future
------
@@ -104,11 +129,16 @@ Future
original dict (after ``__getattr__`` deprecation period, it was deprecated
in 1.2).
-- 1.5: Remove ``pyramid.requests.DeprecatedRequestMethodsMixin``.
+- 1.5: Remove ``pyramid.requests.DeprecatedRequestMethodsMixin`` and code in
+ renderers module that looks for _response_content_type, et. al.
- 1.5: Maybe? deprecate set_request_property in favor of pointing people at
add_request_method, schedule removal for 1.8?
+- 1.5: Remove pyramid.config.rendering set_renderer_globals_factory maybe.
+
+- 1.5: remove pyramid.config.route _add_view_from_route function.
+
- 1.6: Remove IContextURL and TraversalContextURL.
Probably Bad Ideas
@@ -142,3 +172,5 @@ Probably Bad Ideas
with config.partial(introspection=False) as c:
c.add_view(..)
+- _fix_registry should dictify the registry being fixed.
+
diff --git a/docs/Makefile b/docs/Makefile
index e4a325022..c98fdc65e 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -25,7 +25,7 @@ help:
clean:
-rm -rf _build/*
-html: _themes
+html: themes
mkdir -p _build/html _build/doctrees
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
@echo
@@ -47,7 +47,7 @@ pickle:
web: pickle
-htmlhelp: _themes
+htmlhelp: themes
mkdir -p _build/htmlhelp _build/doctrees
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
@echo
@@ -84,5 +84,5 @@ epub:
@echo
@echo "Build finished. The epub file is in _build/epub."
-_themes:
- git submodule update --init
+themes:
+ cd ..; git submodule update --init --recursive; cd docs;
diff --git a/docs/api.rst b/docs/api.rst
index e33fd6a74..9e540b49b 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -12,6 +12,7 @@ documentation is organized alphabetically by module name.
api/authentication
api/compat
api/config
+ api/decorator
api/events
api/exceptions
api/httpexceptions
diff --git a/docs/api/authentication.rst b/docs/api/authentication.rst
index ed96e9c98..e6ee5658b 100644
--- a/docs/api/authentication.rst
+++ b/docs/api/authentication.rst
@@ -11,12 +11,24 @@ Authentication Policies
.. autoclass:: SHA512AuthTktAuthenticationPolicy
.. autoclass:: AuthTktAuthenticationPolicy
-
- .. autoclass:: RepozeWho1AuthenticationPolicy
+ :members:
+ :inherited-members:
.. autoclass:: RemoteUserAuthenticationPolicy
+ :members:
+ :inherited-members:
.. autoclass:: SessionAuthenticationPolicy
+ :members:
+ :inherited-members:
+
+ .. autoclass:: BasicAuthAuthenticationPolicy
+ :members:
+ :inherited-members:
+
+ .. autoclass:: RepozeWho1AuthenticationPolicy
+ :members:
+ :inherited-members:
Helper Classes
~~~~~~~~~~~~~~
diff --git a/docs/api/decorator.rst b/docs/api/decorator.rst
new file mode 100644
index 000000000..35d9131df
--- /dev/null
+++ b/docs/api/decorator.rst
@@ -0,0 +1,9 @@
+.. _decorator_module:
+
+:mod:`pyramid.decorator`
+--------------------------
+
+.. automodule:: pyramid.decorator
+
+.. autofunction:: reify
+
diff --git a/docs/api/request.rst b/docs/api/request.rst
index 1112ea069..3a1439874 100644
--- a/docs/api/request.rst
+++ b/docs/api/request.rst
@@ -161,6 +161,62 @@
request, the value of this attribute will be ``None``. See
:ref:`matched_route`.
+ .. method:: invoke_subrequest(request, use_tweens=False)
+
+ .. warning::
+
+ This API was added in Pyramid 1.4a1.
+
+ Obtain a response object from the Pyramid application based on
+ information in the ``request`` object provided. The ``request`` object
+ must be an object that implements the Pyramid request interface (such
+ as a :class:`pyramid.request.Request` instance). If ``use_tweens`` is
+ ``True``, the request will be sent to the :term:`tween` in the tween
+ stack closest to the request ingress. If ``use_tweens`` is ``False``,
+ the request will be sent to the main router handler, and no tweens will
+ be invoked.
+
+ This function also:
+
+ - manages the threadlocal stack (so that
+ :func:`~pyramid.threadlocal.get_current_request` and
+ :func:`~pyramid.threadlocal.get_current_registry` work during a
+ request)
+
+ - Adds a ``registry`` attribute (the current Pyramid registry) and a
+ ``invoke_subrequest`` attribute (a callable) to the request object it's
+ handed.
+
+ - sets request extensions (such as those added via
+ :meth:`~pyramid.config.Configurator.add_request_method` or
+ :meth:`~pyramid.config.Configurator.set_request_property`) on the
+ request it's passed.
+
+ - causes a :class:`~pyramid.event.NewRequest` event to be sent at the
+ beginning of request processing.
+
+ - causes a :class:`~pyramid.event.ContextFound` event to be sent
+ when a context resource is found.
+
+ - Ensures that the user implied by the request passed has the necessary
+ authorization to invoke view callable before calling it.
+
+ - causes a :class:`~pyramid.event.NewResponse` event to be sent when
+ the Pyramid application returns a response.
+
+ - Calls any :term:`response callback` functions defined within the
+ request's lifetime if a response is obtained from the Pyramid
+ application.
+
+ - Calls any :term:`finished callback` functions defined within the
+ request's lifetime.
+
+ ``invoke_subrequest`` isn't *actually* a method of the Request object;
+ it's a callable added when the Pyramid router is invoked, or when a
+ subrequest is invoked. This means that it's not available for use on a
+ request provided by e.g. the ``pshell`` environment. For more
+ information, see :ref:`subrequest_chapter`.
+
.. automethod:: add_response_callback
.. automethod:: add_finished_callback
diff --git a/docs/api/session.rst b/docs/api/session.rst
index 44b4bd860..31bc196ad 100644
--- a/docs/api/session.rst
+++ b/docs/api/session.rst
@@ -11,4 +11,6 @@
.. autofunction:: signed_deserialize
+ .. autofunction:: check_csrf_token
+
diff --git a/docs/api/testing.rst b/docs/api/testing.rst
index f388dc263..1366a1795 100644
--- a/docs/api/testing.rst
+++ b/docs/api/testing.rst
@@ -9,6 +9,8 @@
.. autofunction:: tearDown
+ .. autofunction:: testConfig(registry=None, request=None, hook_zca=True, autocommit=True, settings=None)
+
.. autofunction:: cleanUp
.. autoclass:: DummyResource
diff --git a/docs/changes.rst b/docs/changes.rst
index 6294123ed..fdeaf1e99 100644
--- a/docs/changes.rst
+++ b/docs/changes.rst
@@ -1,3 +1,5 @@
+.. _changelog:
+
:app:`Pyramid` Change History
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/conf.py b/docs/conf.py
index 80ee0d2e5..9bda4c798 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -81,7 +81,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year
# other places throughout the built documents.
#
# The short X.Y version.
-version = '1.4dev'
+version = '1.4a3'
# The full version, including alpha/beta/rc tags.
release = version
@@ -160,7 +160,7 @@ html_theme_path = ['_themes']
html_theme = 'pyramid'
html_theme_options = dict(
github_url='https://github.com/Pylons/pyramid',
- in_progress='true',
+# in_progress='true',
)
# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
diff --git a/docs/designdefense.rst b/docs/designdefense.rst
index d896022e6..b468ddac6 100644
--- a/docs/designdefense.rst
+++ b/docs/designdefense.rst
@@ -390,20 +390,6 @@ completely optional. No ZCML is required at all to use :app:`Pyramid`, nor
any other sort of frameworky declarative frontend to application
configuration.
-.. _model_traversal_confusion:
-
-Pyramid Uses "Model" To Represent A Node In The Graph of Objects Traversed
---------------------------------------------------------------------------
-
-The ``repoze.bfg`` documentation used to refer to the graph being traversed
-when :term:`traversal` is used as a "model graph". A terminology overlap
-confused people who wrote applications that always use ORM packages such as
-SQLAlchemy, which has a different notion of the definition of a "model". As
-a result, in Pyramid 1.0a7, the tree of objects traversed is now renamed to
-:term:`resource tree` and its components are now named :term:`resource`
-objects. Associated APIs have been changed. This hopefully alleviates the
-terminology confusion caused by overriding the term "model".
-
Pyramid Does Traversal, And I Don't Like Traversal
--------------------------------------------------
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 34cf1b078..adcf36f7c 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -41,14 +41,20 @@ Glossary
setuptools
`Setuptools <http://peak.telecommunity.com/DevCenter/setuptools>`_
builds on Python's ``distutils`` to provide easier building,
- distribution, and installation of libraries and applications.
+ distribution, and installation of libraries and applications. As of
+ this writing, setuptools runs under Python 2, but not under Python 3.
+ You can use :term:`distribute` under Python 3 instead.
+
+ distribute
+ `Distribute <http://packages.python.org/distribute/>`_ is a fork of
+ :term:`setuptools` which runs on both Python 2 and Python 3.
pkg_resources
- A module which ships with :term:`setuptools` that provides an API for
- addressing "asset files" within a Python :term:`package`. Asset files
- are static files, template files, etc; basically anything
- non-Python-source that lives in a Python package can be considered a
- asset file. See also `PkgResources
+ A module which ships with :term:`setuptools` and :term:`distribute` that
+ provides an API for addressing "asset files" within a Python
+ :term:`package`. Asset files are static files, template files, etc;
+ basically anything non-Python-source that lives in a Python package can
+ be considered a asset file. See also `PkgResources
<http://peak.telecommunity.com/DevCenter/PkgResources>`_
asset
@@ -84,7 +90,7 @@ Glossary
(Setuptools/distutils terminology). A file representing an
installable library or application. Distributions are usually
files that have the suffix of ``.egg``, ``.tar.gz``, or ``.zip``.
- Distributions are the target of Setuptools commands such as
+ Distributions are the target of Setuptools-related commands such as
``easy_install``.
entry point
@@ -475,10 +481,24 @@ Glossary
:app:`Pyramid` to form a workflow system.
virtual root
- A resource object representing the "virtual" root of a request; this
- is typically the physical root object (the object returned by the
- application root factory) unless :ref:`vhosting_chapter` is in
- use.
+ A resource object representing the "virtual" root of a request; this is
+ typically the :term:`physical root` object unless :ref:`vhosting_chapter`
+ is in use.
+
+ physical root
+ The object returned by the application :term:`root factory`. Unlike the
+ the :term:`virtual root` of a request, it is not impacted by
+ :ref:`vhosting_chapter`: it will always be the actual object returned by
+ the root factory, never a subobject.
+
+ physical path
+ The path required by a traversal which resolve a :term:`resource` starting
+ from the :term:`physical root`. For example, the physical path of the
+ ``abc`` subobject of the physical root object is ``/abc``. Physical paths
+ can also be specified as tuples where the first element is the empty
+ string (representing the root), and every other element is a Unicode
+ object, e.g. ``('', 'abc')``. Physical paths are also sometimes called
+ "traversal paths".
lineage
An ordered sequence of objects based on a ":term:`location` -aware"
@@ -812,9 +832,12 @@ Glossary
application.
session factory
- A callable, which, when called with a single argument named
- ``request`` (a :term:`request` object), returns a
- :term:`session` object.
+ A callable, which, when called with a single argument named ``request``
+ (a :term:`request` object), returns a :term:`session` object. See
+ :ref:`using_the_default_session_factory`,
+ :ref:`using_alternate_session_factories` and
+ :meth:`pyramid.config.Configurator.set_session_factory` for more
+ information.
Mako
`Mako <http://www.makotemplates.org/>`_ is a template language language
diff --git a/docs/index.rst b/docs/index.rst
index c84314274..faf8258c1 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -37,6 +37,7 @@ What's New
.. toctree::
:maxdepth: 1
+ whatsnew-1.4
whatsnew-1.3
whatsnew-1.2
whatsnew-1.1
@@ -82,12 +83,14 @@ Narrative documentation in chapter form explaining how to use
narr/traversal
narr/security
narr/hybrid
+ narr/subrequest
narr/hooks
narr/introspector
narr/extending
narr/advconfig
narr/extconfig
narr/scaffolding
+ narr/upgrading
narr/threadlocals
narr/zca
diff --git a/docs/latexindex.rst b/docs/latexindex.rst
index 99ec2f54d..6bb875f73 100644
--- a/docs/latexindex.rst
+++ b/docs/latexindex.rst
@@ -54,6 +54,7 @@ Narrative Documentation
narr/traversal
narr/security
narr/hybrid
+ narr/subrequest
narr/hooks
narr/introspector
narr/extending
@@ -85,8 +86,6 @@ API Reference
api/authorization
api/authentication
- api/chameleon_text
- api/chameleon_zpt
api/config
api/events
api/exceptions
diff --git a/docs/narr/MyProject/myproject/static/pylons.css b/docs/narr/MyProject/myproject/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/docs/narr/MyProject/myproject/static/pylons.css
+++ b/docs/narr/MyProject/myproject/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/narr/advconfig.rst b/docs/narr/advconfig.rst
index 165cf7474..ad22a82c9 100644
--- a/docs/narr/advconfig.rst
+++ b/docs/narr/advconfig.rst
@@ -414,3 +414,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.
+More Information
+----------------
+
+For more information, see the article entitled `"A Whirlwind Rour of Advanced
+Configuration Tactics"
+<http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/configuration/whirlwind_tour.html>`_
+in the Pyramid Cookbook.
diff --git a/docs/narr/install.rst b/docs/narr/install.rst
index 172cfd6d3..e8482a289 100644
--- a/docs/narr/install.rst
+++ b/docs/narr/install.rst
@@ -14,13 +14,13 @@ run :app:`Pyramid`.
.. sidebar:: Python Versions
- As of this writing, :app:`Pyramid` has been tested under Python 2.6.6,
- Python 2.7.2, and Python 3.2. :app:`Pyramid` does not run under any
- version of Python before 2.6.
+ As of this writing, :app:`Pyramid` has been tested under Python 2.6.8,
+ Python 2.7.3, Python 3.2.3, and Python 3.3b1. :app:`Pyramid` does not
+ run under any version of Python before 2.6.
: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 :term:`PyPy` (1.6+).
+known to run on :term:`PyPy` (1.9+).
:app:`Pyramid` installation does not require the compilation of any
C code, so you need only a Python interpreter that meets the
@@ -45,15 +45,15 @@ system's package manager is slightly different, but the "flavor" of
them is usually the same.
For example, on an Ubuntu Linux system, to use the system package
-manager to install a Python 2.6 interpreter, use the following
+manager to install a Python 2.7 interpreter, use the following
command:
.. code-block:: text
- $ sudo apt-get install python2.6-dev
+ $ sudo apt-get install python2.7-dev
Once these steps are performed, the Python interpreter will usually be
-invokable via ``python2.6`` from a shell prompt.
+invokable via ``python2.7`` from a shell prompt.
.. index::
pair: install; Python (from source, UNIX)
@@ -80,7 +80,7 @@ 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, you can
-install a Python 2.6 interpreter from *source*, on the same system,
+install a Python 2.7 interpreter from *source*, on the same system,
using the following commands:
.. code-block:: text
@@ -90,15 +90,15 @@ using the following commands:
[chrism@vitaminf ~]$ mkdir opt
[chrism@vitaminf ~]$ cd tmp
[chrism@vitaminf tmp]$ wget \
- http://www.python.org/ftp/python/2.6.4/Python-2.6.4.tgz
- [chrism@vitaminf tmp]$ tar xvzf Python-2.6.4.tgz
- [chrism@vitaminf tmp]$ cd Python-2.6.4
- [chrism@vitaminf Python-2.6.4]$ ./configure \
- --prefix=$HOME/opt/Python-2.6.4
- [chrism@vitaminf Python-2.6.4]$ make; make install
+ http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tgz
+ [chrism@vitaminf tmp]$ tar xvzf Python-2.7.3.tgz
+ [chrism@vitaminf tmp]$ cd Python-2.7.3
+ [chrism@vitaminf Python-2.7.3]$ ./configure \
+ --prefix=$HOME/opt/Python-2.7.3
+ [chrism@vitaminf Python-2.7.3]$ make; make install
Once these steps are performed, the Python interpreter will be
-invokable via ``$HOME/opt/Python-2.6.4/bin/python`` from a shell
+invokable via ``$HOME/opt/Python-2.7.3/bin/python`` from a shell
prompt.
.. index::
@@ -108,7 +108,7 @@ If You Don't Yet Have A Python Interpreter (Windows)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If your Windows system doesn't have a Python interpreter, you'll need
-to install it by downloading a Python 2.6-series interpreter
+to install it by downloading a Python 2.7-series interpreter
executable from `python.org's download section
<http://python.org/download/>`_ (the files labeled "Windows
Installer"). Once you've downloaded it, double click on the
@@ -119,7 +119,7 @@ extensions <http://sourceforge.net/projects/pywin32/files/>`_.
.. warning::
After you install Python on Windows, you may need to add the
- ``C:\Python26`` directory to your environment's ``Path`` in order
+ ``C:\Python27`` directory to your environment's ``Path`` in order
to make it possible to invoke Python from a command prompt by
typing ``python``. To do so, right click ``My Computer``, select
``Properties`` --> ``Advanced Tab`` --> ``Environment Variables``
@@ -141,39 +141,62 @@ done by using the :term:`virtualenv` package. Using a virtualenv will
also prevent :app:`Pyramid` from globally installing versions of
packages that are not compatible with your system Python.
-To set up a virtualenv in which to install :app:`Pyramid`, first
-ensure that :term:`setuptools` is installed. Invoke ``import
-setuptools`` within the Python interpreter you'd like to run
-:app:`Pyramid` under:
+To set up a virtualenv in which to install :app:`Pyramid`, first ensure that
+:term:`setuptools` or :term:`distribute` is installed. To do so, invoke
+``import setuptools`` within the Python interpreter you'd like to run
+:app:`Pyramid` under.
+
+Here's the output you'll expect if setuptools or distribute is already
+installed:
.. code-block:: text
- [chrism@vitaminf pyramid]$ python
- Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32)
- [GCC 4.4.3] on linux2
+ [chrism@thinko docs]$ python2.7
+ Python 2.7.3 (default, Aug 1 2012, 05:14:39)
+ [GCC 4.6.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.6-series
-interpreter on Mac OS X; your output may differ if you're using a
-later Python version or a different platform.
-
-If you are using a "system" Python (one installed by your OS
-distributor or a 3rd-party packager such as Fink or MacPorts), you can
-usually install the setuptools package by using your system's package
-manager. If you cannot do this, or if you're using a self-installed
-version of Python, you will need to install setuptools "by hand".
-Installing setuptools "by hand" is always a reasonable thing to do,
-even if your package manager already has a pre-chewed version of
-setuptools for installation.
-
-To install setuptools by hand, first download `ez_setup.py
-<http://peak.telecommunity.com/dist/ez_setup.py>`_ then invoke it
-using the Python interpreter into which you want to install
-setuptools.
+Here's the output you can expect if setuptools or distribute is not already
+installed:
+
+.. code-block:: text
+
+ [chrism@thinko docs]$ python2.7
+ Python 2.7.3 (default, Aug 1 2012, 05:14:39)
+ [GCC 4.6.3] on linux2
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>> import setutptools
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ ImportError: No module named setutptools
+ >>>
+
+If ``import setuptools`` raises an :exc:`ImportError` as it does above, you
+will need to install setuptools or distribute manually. Note that above
+we're using a Python 2.7-series interpreter on Mac OS X; your output may
+differ if you're using a later Python version or a different platform.
+
+If you are using a "system" Python (one installed by your OS distributor or a
+3rd-party packager such as Fink or MacPorts), you can usually install the
+setuptools or distribute package by using your system's package manager. If
+you cannot do this, or if you're using a self-installed version of Python,
+you will need to install setuptools or distribute "by hand". Installing
+setuptools or distribute "by hand" is always a reasonable thing to do, even
+if your package manager already has a pre-chewed version of setuptools for
+installation.
+
+If you're using Python 2, you'll want to install ``setuptools``. If you're
+using Python 3, you'll want to install ``distribute``. Below we tell you how
+to do both.
+
+Installing Setuptools On Python 2
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To install setuptools by hand under Python 2, first download `ez_setup.py
+<http://peak.telecommunity.com/dist/ez_setup.py>`_ then invoke it using the
+Python interpreter into which you want to install setuptools.
.. code-block:: text
@@ -188,16 +211,37 @@ the script. To remediate this, you may need to do:
$ sudo python ez_setup.py
+Installing Distribute On Python 3
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``setuptools`` doesn't work under Python 3. Instead, you can use
+``distribute``, which is a fork of setuptools that does work on Python 3. To
+install it, first download `distribute_setup.py
+<http://python-distribute.org/distribute_setup.py>`_ then invoke it using the
+Python interpreter into which you want to install setuptools.
+
+.. code-block:: text
+
+ $ python3 distribute_setup.py
+
+Once this command is invoked, distribute should be installed on your system.
+If the command fails due to permission errors, you may need to be the
+administrative user on your system to successfully invoke the script. To
+remediate this, you may need to do:
+
+.. code-block:: text
+
+ $ sudo python3 distribute_setup.py
+
.. index::
pair: install; virtualenv
Installing the ``virtualenv`` Package
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Once you've got setuptools installed, you should install the
-:term:`virtualenv` package. To install the :term:`virtualenv` package
-into your setuptools-enabled Python interpreter, use the
-``easy_install`` command.
+Once you've got setuptools or distribute installed, you should install the
+:term:`virtualenv` package. To install the :term:`virtualenv` package into
+your setuptools-enabled Python interpreter, use the ``easy_install`` command.
.. code-block:: text
@@ -269,35 +313,90 @@ complete, as it downloads and installs a number of dependencies.
Installing :app:`Pyramid` on a Windows System
-------------------------------------------------
-#. Install, or find `Python 2.6
- <http://python.org/download/releases/2.6.4/>`_ for your system.
+You can use Pyramid on Windows under Python 2 or under Python 3. Directions
+for both versions are included below.
+
+Windows Using Python 2
+~~~~~~~~~~~~~~~~~~~~~~
+
+#. Install, or find `Python 2.7
+ <http://www.python.org/download/releases/2.7.3/>`_ for your system.
#. Install the `Python for Windows extensions
<http://sourceforge.net/projects/pywin32/files/>`_. Make sure to
- pick the right download for Python 2.6 and install it using the
+ pick the right download for Python 2.7 and install it using the
same Python installation from the previous step.
#. Install latest :term:`setuptools` distribution into the Python you
obtained/installed/found in the step above: download `ez_setup.py
<http://peak.telecommunity.com/dist/ez_setup.py>`_ and run it using
- the ``python`` interpreter of your Python 2.6 installation using a
+ the ``python`` interpreter of your Python 2.7 installation using a
command prompt:
.. code-block:: text
- c:\> c:\Python26\python ez_setup.py
+ c:\> c:\Python27\python ez_setup.py
+
+#. Use that Python's `bin/easy_install` to install `virtualenv`:
+
+ .. code-block:: text
+
+ c:\> c:\Python27\Scripts\easy_install virtualenv
+
+#. Use that Python's virtualenv to make a workspace:
+
+ .. code-block:: text
+
+ c:\> c:\Python27\Scripts\virtualenv --no-site-packages env
+
+#. Switch to the ``env`` directory:
+
+ .. code-block:: text
+
+ c:\> cd env
+
+#. (Optional) Consider using ``Scripts\activate.bat`` to make your shell
+ environment wired to use the virtualenv.
+
+#. Use ``easy_install`` to get :app:`Pyramid` and its direct dependencies
+ installed:
+
+ .. code-block:: text
+
+ c:\env> Scripts\easy_install pyramid
+
+Windows Using Python 3
+~~~~~~~~~~~~~~~~~~~~~~
+
+#. Install, or find `Python 3.2
+ <http://www.python.org/download/releases/3.2.3/>`_ for your system.
+
+#. Install the `Python for Windows extensions
+ <http://sourceforge.net/projects/pywin32/files/>`_. Make sure to
+ pick the right download for Python 3.2 and install it using the
+ same Python installation from the previous step.
+
+#. Install latest :term:`distribute` distribution into the Python you
+ obtained/installed/found in the step above: download `distribute_setup.py
+ <http://python-distribute.org/distribute_setup.py>`_ and run it using the
+ ``python`` interpreter of your Python 3.2 installation using a command
+ prompt:
+
+ .. code-block:: text
+
+ c:\> c:\Python32\python distribute_setup.py
#. Use that Python's `bin/easy_install` to install `virtualenv`:
.. code-block:: text
- c:\> c:\Python26\Scripts\easy_install virtualenv
+ c:\> c:\Python32\Scripts\easy_install virtualenv
#. Use that Python's virtualenv to make a workspace:
.. code-block:: text
- c:\> c:\Python26\Scripts\virtualenv --no-site-packages env
+ c:\> c:\Python32\Scripts\virtualenv --no-site-packages env
#. Switch to the ``env`` directory:
@@ -308,8 +407,8 @@ Installing :app:`Pyramid` on a Windows System
#. (Optional) Consider using ``Scripts\activate.bat`` to make your shell
environment wired to use the virtualenv.
-#. Use ``easy_install`` pointed at the "current" index to get
- :app:`Pyramid` and its direct dependencies installed:
+#. Use ``easy_install`` to get :app:`Pyramid` and its direct dependencies
+ installed:
.. code-block:: text
diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst
index 7c0f9223f..b35c61720 100644
--- a/docs/narr/introduction.rst
+++ b/docs/narr/introduction.rst
@@ -315,7 +315,7 @@ Rendered views can return dictionaries
If you use a :term:`renderer`, you don't have to return a special kind of
"webby" ``Response`` object from a view. Instead, you can return a
-dictionary instead, and Pyramid will take care of converting that dictionary
+dictionary, and Pyramid will take care of converting that dictionary
to a Response using a template on your behalf. This makes the view easier to
test, because you don't have to parse HTML in your tests; just make an
assertion instead that the view returns "the right stuff" in the dictionary
diff --git a/docs/narr/introspector.rst b/docs/narr/introspector.rst
index 6bfaf11c0..b88f3f0c8 100644
--- a/docs/narr/introspector.rst
+++ b/docs/narr/introspector.rst
@@ -393,6 +393,10 @@ introspectables in categories not described here.
The ``match_param`` argument passed to ``add_view``.
+ ``csrf_token``
+
+ The ``csrf_token`` argument passed to ``add_view``.
+
``callable``
The (resolved) ``view`` argument passed to ``add_view``. Represents the
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 1e2c225d2..b08948e43 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -286,11 +286,39 @@ Here's sample output from a run of ``pserve`` on UNIX:
$ ../bin/pserve development.ini
Starting server in PID 16601.
- serving on 0.0.0.0:6543 view at http://127.0.0.1:6543
+ serving on http://0.0.0.0:6543
-By default, :app:`Pyramid` applications generated from a scaffold
-will listen on TCP port 6543. You can shut down a server started this way by
-pressing ``Ctrl-C``.
+When you use ``pserve`` to start the application implied by the default
+rendering of a scaffold, it will respond to requests on *all* IP addresses
+possessed by your system, not just requests to ``localhost``. This is what
+the ``0.0.0.0`` in ``serving on http://0.0.0.0:6543`` means. The server will
+respond to requests made to ``127.0.0.1`` and on any external IP address.
+For example, your system might be configured to have an external IP address
+``192.168.1.50``. If that's the case, if you use a browser running on the
+same system as Pyramid, it will be able to access the application via
+``http://127.0.0.1:6543/`` as well as via
+``http://129.168.1.50:6543/``. However, *other people* on other computers on
+the same network will also be able to visit your Pyramid application in their
+browser by visiting ``http://192.168.1.50:6543/``.
+
+If you want to restrict access such that only a browser running on the same
+machine as Pyramid will be able to access your Pyramid application, edit the
+``development.ini`` file, and replace the ``host`` value in the
+``[server:main]`` section. Change it from ``0.0.0.0`` to ``127.0.0.1``. For
+example::
+
+ [server:main]
+ use = egg:waitress#main
+ host = 127.0.0.1
+ port = 6543
+
+You can change the port on which the server runs on by changing the same
+portion of the ``development.ini`` file. For example, you can change the
+``port = 6543`` line in the ``development.ini`` file's ``[server:main]``
+section to ``port = 8080`` to run the server on port 8080 instead of
+port 6543.
+
+You can shut down a server started this way by pressing ``Ctrl-C``.
The default server used to run your Pyramid application when a project is
created from a scaffold is named :term:`Waitress`. This server is what
@@ -309,11 +337,6 @@ under the default server, it will almost certainly work under any other
server in production if you eventually choose to use a different one. Don't
worry about it right now.
-You can change the port on which the server runs on by changing the
-``development.ini`` file. For example, you can change the ``port = 6543``
-line in the ``development.ini`` file's ``[server:main]`` section to ``port =
-8080`` to run the server on port 8080 instead of port 6543.
-
For more detailed information about the startup process, see
:ref:`startup_chapter`. For more information about environment variables and
configuration file settings that influence startup and runtime behavior, see
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index 63287e2cd..1158d2225 100644
--- a/docs/narr/renderers.rst
+++ b/docs/narr/renderers.rst
@@ -329,7 +329,7 @@ time "by hand". Configure a JSONP renderer using the
Once this renderer is registered via
:meth:`~pyramid.config.Configurator.add_renderer` as above, you can use
``jsonp`` as the ``renderer=`` parameter to ``@view_config`` or
-:meth:`pyramid.config.Configurator.add_view``:
+:meth:`pyramid.config.Configurator.add_view`:
.. code-block:: python
diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst
index 1aa1b6341..f7da7838e 100644
--- a/docs/narr/sessions.rst
+++ b/docs/narr/sessions.rst
@@ -63,10 +63,15 @@ application by using the ``session_factory`` argument to the
this implementation is, by default, *unencrypted*. You should not use it
when you keep sensitive information in the session object, as the
information can be easily read by both users of your application and third
- parties who have access to your users' network traffic. Use a different
- session factory implementation (preferably one which keeps session data on
- the server) for anything but the most basic of applications where "session
- security doesn't matter".
+ parties who have access to your users' network traffic. And if you use this
+ sessioning implementation, and you inadvertently create a cross-site
+ scripting vulnerability in your application, because the session data is
+ stored unencrypted in a cookie, it will also be easier for evildoers to
+ obtain the current user's cross-site scripting token. In short, use a
+ different session factory implementation (preferably one which keeps session
+ data on the server) for anything but the most basic of applications where
+ "session security doesn't matter", and you are sure your application has no
+ cross-site scripting vulnerabilities.
.. index::
single: session object
diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst
new file mode 100644
index 000000000..89551ab35
--- /dev/null
+++ b/docs/narr/subrequest.rst
@@ -0,0 +1,271 @@
+.. index::
+ single: subrequest
+
+.. _subrequest_chapter:
+
+Invoking a Subrequest
+=====================
+
+.. warning::
+
+ This feature was added in Pyramid 1.4a1.
+
+:app:`Pyramid` allows you to invoke a subrequest at any point during the
+processing of a request. Invoking a subrequest allows you to obtain a
+:term:`response` object from a view callable within your :app:`Pyramid`
+application while you're executing a different view callable within the same
+application.
+
+Here's an example application which uses a subrequest:
+
+.. code-block:: python
+
+ from wsgiref.simple_server import make_server
+ from pyramid.config import Configurator
+ from pyramid.request import Request
+
+ def view_one(request):
+ subreq = Request.blank('/view_two')
+ response = request.invoke_subrequest(subreq)
+ return response
+
+ def view_two(request):
+ request.response.body = 'This came from view_two'
+ return request.response
+
+ if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('one', '/view_one')
+ config.add_route('two', '/view_two')
+ config.add_view(view_one, route_name='one')
+ config.add_view(view_two, route_name='two')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
+
+When ``/view_one`` is visted in a browser, the text printed in the browser
+pane will be ``This came from view_two``. The ``view_one`` view used the
+:meth:`pyramid.request.Request.invoke_subrequest` API to obtain a response
+from another view (``view_two``) within the same application when it
+executed. It did so by constructing a new request that had a URL that it
+knew would match the ``view_two`` view registration, and passed that new
+request along to :meth:`pyramid.request.Request.invoke_subrequest`. The
+``view_two`` view callable was invoked, and it returned a response. The
+``view_one`` view callable then simply returned the response it obtained from
+the ``view_two`` view callable.
+
+Note that it doesn't matter if the view callable invoked via a subrequest
+actually returns a *literal* Response object. Any view callable that uses a
+renderer or which returns an object that can be interpreted by a response
+adapter when found and invoked via
+:meth:`pyramid.request.Request.invoke_subrequest` will return a Response
+object:
+
+.. code-block:: python
+
+ from wsgiref.simple_server import make_server
+ from pyramid.config import Configurator
+ from pyramid.request import Request
+
+ def view_one(request):
+ subreq = Request.blank('/view_two')
+ response = request.invoke_subrequest(subreq)
+ return response
+
+ def view_two(request):
+ return 'This came from view_two'
+
+ if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('one', '/view_one')
+ config.add_route('two', '/view_two')
+ config.add_view(view_one, route_name='one')
+ config.add_view(view_two, route_name='two', renderer='string')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
+
+Even though the ``view_two`` view callable returned a string, it was invoked
+in such a way that the ``string`` renderer associated with the view
+registration that was found turned it into a "real" response object for
+consumption by ``view_one``.
+
+Being able to unconditionally obtain a response object by invoking a view
+callable indirectly is the main advantage to using
+:meth:`pyramid.request.Request.invoke_subrequest` instead of simply importing
+a view callable and executing it directly. Note that there's not much
+advantage to invoking a view using a subrequest if you *can* invoke a view
+callable directly. Subrequests are slower and are less convenient if you
+actually do want just the literal information returned by a function that
+happens to be a view callable.
+
+Note that, by default, if a view callable invoked by a subrequest raises an
+exception, the exception will be raised to the caller of
+:meth:`~pyramid.request.Request.invoke_subrequest` even if you have a
+:term:`exception view` configured:
+
+.. code-block:: python
+
+ from wsgiref.simple_server import make_server
+ from pyramid.config import Configurator
+ from pyramid.request import Request
+
+ def view_one(request):
+ subreq = Request.blank('/view_two')
+ response = request.invoke_subrequest(subreq)
+ return response
+
+ def view_two(request):
+ raise ValueError('foo')
+
+ def excview(request):
+ request.response.body = b'An exception was raised'
+ request.response.status_int = 500
+ return request.response
+
+ if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('one', '/view_one')
+ config.add_route('two', '/view_two')
+ config.add_view(view_one, route_name='one')
+ config.add_view(view_two, route_name='two', renderer='string')
+ config.add_view(excview, context=Exception)
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
+
+When we run the above code and visit ``/view_one`` in a browser, the
+``excview`` :term:`exception view` will *not* be executed. Instead, the call
+to :meth:`~pyramid.request.Request.invoke_subrequest` will cause a
+:exc:`ValueError` exception to be raised and a response will never be
+generated. We can change this behavior; how to do so is described below in
+our discussion of the ``use_tweens`` argument.
+
+The :meth:`pyramid.request.Request.invoke_subrequest` API accepts two
+arguments: a positional argument ``request`` that must be provided, and and
+``use_tweens`` keyword argument that is optional; it defaults to ``False``.
+
+The ``request`` object passed to the API must be an object that implements
+the Pyramid request interface (such as a :class:`pyramid.request.Request`
+instance). If ``use_tweens`` is ``True``, the request will be sent to the
+:term:`tween` in the tween stack closest to the request ingress. If
+``use_tweens`` is ``False``, the request will be sent to the main router
+handler, and no tweens will be invoked.
+
+In the example above, the call to
+:meth:`~pyramid.request.Request.invoke_subrequest` will always raise an
+exception. This is because it's using the default value for ``use_tweens``,
+which is ``False``. You can pass ``use_tweens=True`` instead to ensure that
+it will convert an exception to a Response if an :term:`exception view` is
+configured instead of raising the exception. This because exception views
+are called by the exception view :term:`tween` as described in
+:ref:`exception_views` when any view raises an exception.
+
+We can cause the subrequest to be run through the tween stack by passing
+``use_tweens=True`` to the call to
+:meth:`~pyramid.request.Request.invoke_subrequest`, like this:
+
+.. code-block:: python
+
+ from wsgiref.simple_server import make_server
+ from pyramid.config import Configurator
+ from pyramid.request import Request
+
+ def view_one(request):
+ subreq = Request.blank('/view_two')
+ response = request.invoke_subrequest(subreq, use_tweens=True)
+ return response
+
+ def view_two(request):
+ raise ValueError('foo')
+
+ def excview(request):
+ request.response.body = b'An exception was raised'
+ request.response.status_int = 500
+ return request.response
+
+ if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('one', '/view_one')
+ config.add_route('two', '/view_two')
+ config.add_view(view_one, route_name='one')
+ config.add_view(view_two, route_name='two', renderer='string')
+ config.add_view(excview, context=Exception)
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 8080, app)
+ server.serve_forever()
+
+In the above case, the call to ``request.invoke_subrequest(subreq)`` will not
+raise an exception. Instead, it will retrieve a "500" response from the
+attempted invocation of ``view_two``, because the tween which invokes an
+exception view to generate a response is run, and therefore ``excview`` is
+executed.
+
+This is one of the major differences between specifying the
+``use_tweens=True`` and ``use_tweens=False`` arguments to
+:meth:`~pyramid.request.Request.invoke_subrequest`. ``use_tweens=True`` may
+also imply invoking transaction commit/abort for the logic executed in the
+subrequest if you've got ``pyramid_tm`` in the tween list, injecting debug
+HTML if you've got ``pyramid_debugtoolbar`` in the tween list, and other
+tween-related side effects as defined by your particular tween list.
+
+The :meth:`~pyramid.request.Request.invoke_subrequest` function also
+unconditionally:
+
+- manages the threadlocal stack so that
+ :func:`~pyramid.threadlocal.get_current_request` and
+ :func:`~pyramid.threadlocal.get_current_registry` work during a request
+ (they will return the subrequest instead of the original request)
+
+- Adds a ``registry`` attribute and a ``invoke_subrequest`` attribute (a
+ callable) to the request object it's handed.
+
+- sets request extensions (such as those added via
+ :meth:`~pyramid.config.Configurator.add_request_method` or
+ :meth:`~pyramid.config.Configurator.set_request_property`) on the subrequest
+ object passed as ``request``
+
+- causes a :class:`~pyramid.event.NewRequest` event to be sent at the
+ beginning of request processing.
+
+- causes a :class:`~pyramid.event.ContextFound` event to be sent when a
+ context resource is found.
+
+- Ensures that the user implied by the request passed has the necessary
+ authorization to invoke view callable before calling it.
+
+- causes a :class:`~pyramid.event.NewResponse` event to be sent when the
+ Pyramid application returns a response.
+
+- Calls any :term:`response callback` functions defined within the subrequest's
+ lifetime if a response is obtained from the Pyramid application.
+
+- Calls any :term:`finished callback` functions defined within the subrequest's
+ lifetime.
+
+The invocation of a subrequest has more or less exactly the same effect as
+the invocation of a request received by the Pyramid router from a web client
+when ``use_tweens=True``. When ``use_tweens=False``, the tweens are skipped
+but all the other steps take place.
+
+It's a poor idea to use the original ``request`` object as an argument to
+:meth:`~pyramid.request.Request.invoke_subrequest`. You should construct a
+new request instead as demonstrated in the above example, using
+:meth:`pyramid.request.Request.blank`. Once you've constructed a request
+object, you'll need to massage the it to match the view callable you'd like
+to be executed during the subrequest. This can be done by adjusting the
+subrequest's URL, its headers, its request method, and other attributes. The
+documentation for :class:`pyramid.request.Request` exposes the methods you
+should call and attributes you should set on the request you create to
+massage it into something that will actually match the view you'd like to
+call via a subrequest.
+
+We've demonstrated use of a subrequest from within a view callable, but you
+can use the :meth:`~pyramid.request.Request.invoke_subrequest` API from
+within a tween or an event handler as well. It's usually a poor idea to
+invoke :meth:`~pyramid.request.Request.invoke_subrequest` from within a
+tween, because tweens already by definition have access to a function that
+will cause a subrequest (they are passed a ``handle`` function), but you can
+do it. It's fine to invoke
+:meth:`~pyramid.request.Request.invoke_subrequest` from within an event
+handler, however.
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index 656cf4773..ba72ebfbf 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -46,20 +46,6 @@ within the body of a view callable like so:
{'foo':1, 'bar':2},
request=request)
-.. warning::
-
- Earlier iterations of this documentation
- (pre-version-1.3) encouraged the application developer to use
- ZPT-specific APIs such as
- :func:`pyramid.chameleon_zpt.render_template_to_response` and
- :func:`pyramid.chameleon_zpt.render_template` to render templates
- directly. This style of rendering still works, but at least for
- purposes of this documentation, those functions are deprecated.
- Application developers are encouraged instead to use the functions
- available in the :mod:`pyramid.renderers` module to perform
- rendering tasks. This set of functions works to render templates
- for all renderer extensions registered with :app:`Pyramid`.
-
The ``sample_view`` :term:`view callable` function above returns a
:term:`response` object which contains the body of the
``templates/foo.pt`` template. In this case, the ``templates``
@@ -79,12 +65,12 @@ prefix on Windows.
.. warning::
Only :term:`Chameleon` templates support defining a renderer for a
- template relative to the location of the module where the view
- callable is defined. Mako templates, and other templating system
- bindings work differently. In particular, Mako templates use a
- "lookup path" as defined by the ``mako.directories`` configuration
- file instead of treating relative paths as relative to the current
- view module. See :ref:`mako_templates`.
+ template relative to the location of the module where the view callable is
+ defined. Mako templates, and other templating system bindings work
+ differently. In particular, Mako templates use a "lookup path" as defined
+ by the ``mako.directories`` configuration file instead of treating
+ relative paths as relative to the current view module. See
+ :ref:`mako_templates`.
The path can alternately be a :term:`asset specification` in the form
``some.dotted.package_name:relative/path``. This makes it possible to
@@ -542,7 +528,7 @@ Sommetime you'd like to render a macro inside of a Chameleon ZPT template
instead of the full Chameleon ZPT template. To render the content of a
``define-macro`` field inside a Chameleon ZPT template, given a Chameleon
template file named ``foo.pt`` and a macro named ``bar`` defined within it
-(e.g. ``<div metal:define-macro="bar">...</div>``, you can configure the
+(e.g. ``<div metal:define-macro="bar">...</div>``), you can configure the
template as a :term:`renderer` like so:
.. code-block:: python
@@ -554,8 +540,8 @@ template as a :term:`renderer` like so:
def my_view(request):
return {'project':'my project'}
-The above will render the ``bar`` macro from within the ``foo.pt`` template
-instead of the entire template.
+The above will render only the ``bar`` macro defined within the ``foo.pt``
+template instead of the entire template.
.. note::
@@ -600,10 +586,6 @@ When the template is rendered, it will show:
Hello, world!
-If you'd rather use templates directly within a view callable (without
-the indirection of using a renderer), see :ref:`chameleon_text_module`
-for the API description.
-
See also :ref:`built_in_renderers` for more general information about
renderers, including Chameleon text renderers.
diff --git a/docs/narr/testing.rst b/docs/narr/testing.rst
index 50e9d5604..20017064b 100644
--- a/docs/narr/testing.rst
+++ b/docs/narr/testing.rst
@@ -157,6 +157,30 @@ We use a "dummy" request implementation supplied by
:class:`pyramid.testing.DummyRequest` because it's easier to construct
than a "real" :app:`Pyramid` request object.
+Test setup using a context manager
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An alternative style of setting up a test configuration is to use the
+`with` statement and :func:`pyramid.testing.testConfig` to create a
+context manager. The context manager will call
+:func:`pyramid.testing.setUp` before the code under test and
+:func:`pyramid.testing.tearDown` afterwards.
+
+This style is useful for small self-contained tests. For example:
+
+.. code-block:: python
+ :linenos:
+
+ import unittest
+
+ class MyTest(unittest.TestCase):
+
+ def test_my_function(self):
+ from pyramid import testing
+ with testing.testConfig() as config:
+ config.add_route('bar', '/bar/{id}')
+ my_function_which_needs_route_bar()
+
What?
~~~~~
diff --git a/docs/narr/upgrading.rst b/docs/narr/upgrading.rst
new file mode 100644
index 000000000..839f59c35
--- /dev/null
+++ b/docs/narr/upgrading.rst
@@ -0,0 +1,234 @@
+.. _upgrading_chapter:
+
+Upgrading Pyramid
+=================
+
+When a new version of Pyramid is released, it will sometimes deprecate a
+feature or remove a feature that was deprecated in an older release. When
+features are removed from Pyramid, applications that depend on those features
+will begin to break. This chapter explains how to ensure your Pyramid
+applications keep working when you upgrade the Pyramid version you're using.
+
+.. sidebar:: About Release Numbering
+
+ Conventionally, application version numbering in Python is described as
+ ``major.minor.micro``. If your Pyramid version is "1.2.3", it means
+ you're running a version of Pyramid with the major version "1", the minor
+ version "2" and the micro version "3". A "major" release is one that
+ increments the first-dot number; 2.X.X might follow 1.X.X. A "minor"
+ release is one that increments the second-dot number; 1.3.X might follow
+ 1.2.X. A "micro" release is one that increments the third-dot number;
+ 1.2.3 might follow 1.2.2. In general, micro releases are "bugfix-only",
+ and contain no new features, minor releases contain new features but are
+ largely backwards compatible with older versions, and a major release
+ indicates a large set of backwards incompatibilities.
+
+The Pyramid core team is conservative when it comes to removing features. We
+don't remove features unnecessarily, but we're human, and we make mistakes
+which cause some features to be evolutionary dead ends. Though we are
+willing to support dead-end features for some amount of time, some eventually
+have to be removed when the cost of supporting them outweighs the benefit of
+keeping them around, because each feature in Pyramid represents a certain
+documentation and maintenance burden.
+
+Deprecation and Removal Policy
+------------------------------
+
+When a feature is scheduled for removal from Pyramid or any of its official
+add-ons, the core development team takes these steps:
+
+- Using the feature will begin to generate a `DeprecationWarning`, indicating
+ the version in which the feature became deprecated.
+
+- A note is added to the documentation indicating that the feature is
+ deprecated.
+
+- A note is added to the :ref:`changelog` about the deprecation.
+
+When a deprecated feature is eventually removed:
+
+- The feature is removed.
+
+- A note is added to the :ref:`changelog` about the removal.
+
+Features are never removed in *micro* releases. They are only removed in
+minor and major releases. Deprecated features are kept around for at least
+*three* minor releases from the time the feature became deprecated.
+Therefore, if a feature is added in Pyramid 1.0, but it's deprecated in
+Pyramid 1.1, it will be kept around through all 1.1.X releases, all 1.2.X
+releases and all 1.3.X releases. It will finally be removed in the first
+1.4.X release.
+
+Sometimes features are "docs-deprecated" instead of formally deprecated.
+This means that the feature will be kept around indefinitely, but it will be
+removed from the documentation or a note will be added to the documentation
+telling folks to use some other newer feature. This happens when the cost of
+keeping an old feature around is very minimal and the support and
+documentation burden is very low. For example, we might rename a function
+that is an API without changing the arguments it accepts. In this case,
+we'll often rename the function, and change the docs to point at the new
+function name, but leave around a backwards compatibility alias to the old
+function name so older code doesn't break.
+
+"Docs deprecated" features tend to work "forever", meaning that they won't be
+removed, and they'll never generate a deprecation warning. However, such
+changes are noted in the :ref:`changelog`, so it's possible to know that you
+should change older spellings to newer ones to ensure that people reading
+your code can find the APIs you're using in the Pyramid docs.
+
+Consulting the Change History
+-----------------------------
+
+Your first line of defense against application failures caused by upgrading
+to a newer Pyramid release is always to read the :ref:`changelog`. to find
+the deprecations and removals for each release between the release you're
+currently running and the one you wish to upgrade to. The change history
+notes every deprecation within a ``Deprecation`` section and every removal
+within a ``Backwards Incompatibilies`` section for each release.
+
+The change history often contains instructions for changing your code to
+avoid deprecation warnings and how to change docs-deprecated spellings to
+newer ones. You can follow along with each deprecation explanation in the
+change history, simply doing a grep or other code search to your application,
+using the change log examples to remediate each potential problem.
+
+.. _testing_under_new_release:
+
+Testing Your Application Under a New Pyramid Release
+----------------------------------------------------
+
+Once you've upgraded your application to a new Pyramid release and you've
+remediated as much as possible by using the change history notes, you'll want
+to run your application's tests (see :ref:`running_tests`) in such a way that
+you can see DeprecationWarnings printed to the console when the tests run.
+
+.. code-block:: bash
+
+ $ python -Wd setup.py test -q
+
+The ``-Wd`` argument is an argument that tells Python to print deprecation
+warnings to the console. Note that the ``-Wd`` flag is only required for
+Python 2.7 and better: Python versions 2.6 and older print deprecation
+warnings to the console by default. See `the Python -W flag documentation
+<http://docs.python.org/using/cmdline.html#cmdoption-W>`_ for more
+information.
+
+As your tests run, deprecation warnings will be printed to the console
+explaining the deprecation and providing instructions about how to prevent
+the deprecation warning from being issued. For example:
+
+.. code-block:: text
+
+ $ python -Wd setup.py test -q
+ # .. elided ...
+ running build_ext
+ /home/chrism/projects/pyramid/env27/myproj/myproj/views.py:3:
+ DeprecationWarning: 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.
+ from pyramid.view import static
+ .
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.014s
+
+ OK
+
+In the above case, it's line #3 in the ``myproj.views`` module (``from
+pyramid.view import static``) that is causing the problem:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import view_config
+
+ from pyramid.view import static
+ myview = static('static', 'static')
+
+The deprecation warning tells me how to fix it, so I can change the code to
+do things the newer way:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view. import view_config
+
+ from pyramid.static import static_view
+ myview = static_view('static', 'static', use_subpath=True)
+
+When I run the tests again, the deprecation warning is no longer printed to
+my console:
+
+.. code-block:: text
+
+ $ python -Wd setup.py test -q
+ # .. elided ...
+ running build_ext
+ .
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.014s
+
+ OK
+
+
+My Application Doesn't Have Any Tests or Has Few Tests
+------------------------------------------------------
+
+If your application has no tests, or has only moderate test coverage, running
+tests won't tell you very much, because the Pyramid codepaths that generate
+deprecation warnings won't be executed.
+
+In this circumstance, you can start your application interactively under a
+server run with the ``PYTHONWARNINGS`` environment variable set to
+``default``. On UNIX, you can do that via:
+
+.. code-block:: bash
+
+ $ PYTHONWARNINGS=default bin/pserve development.ini
+
+On Windows, you need to issue two commands:
+
+.. code-block:: bash
+
+ C:\> set PYTHONWARNINGS=default
+ C:\> Scripts/pserve.exe development.ini
+
+At this point, it's ensured that deprecation warnings will be printed to the
+console whenever a codepath is hit that generates one. You can then click
+around in your application interactively to try to generate them, and
+remediate as explained in :ref:`testing_under_new_release`.
+
+See `the PYTHONWARNINGS environment variable documentation
+<http://docs.python.org/using/cmdline.html#envvar-PYTHONWARNINGS>`_ or `the
+Python -W flag documentation
+<http://docs.python.org/using/cmdline.html#cmdoption-W>`_ for more
+information.
+
+Upgrading to the Very Latest Pyramid Release
+--------------------------------------------
+
+When you upgrade your application to the very most recent Pyramid release,
+it's advisable to upgrade step-wise through each most recent minor release,
+beginning with the one that you know your application currently runs under,
+and ending on the most recent release. For example, if your application is
+running in production on Pyramid 1.2.1, and the most recent Pyramid 1.3
+release is Pyramid 1.3.3, and the most recent Pyramid release is 1.4.4, it's
+advisable to do this:
+
+- Upgrade your environment to the most recent 1.2 release. For example, the
+ most recent 1.2 release might be 1.2.3, so upgrade to it. Then run your
+ application's tests under 1.2.3 as described in
+ :ref:`testing_under_new_release`. Note any deprecation warnings and
+ remediate.
+
+- Upgrade to the most recent 1.3 release, 1.3.3. Run your application's
+ tests, note any deprecation warnings and remediate.
+
+- Upgrade to 1.4.4. Run your application's tests, note any deprecation
+ warnings and remediate.
+
+If you skip testing your application under each minor release (for example if
+you upgrade directly from 1.2.1 to 1.4.4), you might miss a deprecation
+warning and waste more time trying to figure out an error caused by a feature
+removal than it would take to upgrade stepwise through each minor release.
+
+
diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst
index 23b4fde68..6373a8d26 100644
--- a/docs/narr/viewconfig.rst
+++ b/docs/narr/viewconfig.rst
@@ -290,12 +290,13 @@ configured view.
of the ``REQUEST_METHOD`` of the :term:`WSGI` environment.
``request_param``
- This value can be any string. A view declaration with this argument
- ensures that the view will only be called when the :term:`request` has a
- key in the ``request.params`` dictionary (an HTTP ``GET`` or ``POST``
- variable) that has a name which matches the supplied value.
+ This value can be any string or a sequence of strings. A view declaration
+ with this argument ensures that the view will only be called when the
+ :term:`request` has a key in the ``request.params`` dictionary (an HTTP
+ ``GET`` or ``POST`` variable) that has a name which matches the a
+ supplied value.
- If the value supplied has a ``=`` sign in it,
+ If any value supplied has a ``=`` sign in it,
e.g. ``request_param="foo=123"``, then the key (``foo``) must both exist
in the ``request.params`` dictionary, *and* the value must match the right
hand side of the expression (``123``) for the view to "match" the current
@@ -394,6 +395,54 @@ configured view.
consideration when deciding whether or not to invoke the associated view
callable.
+``check_csrf``
+ If specified, this value should be one of ``None``, ``True``, ``False``, or
+ a string representing the 'check name'. If the value is ``True`` or a
+ string, CSRF checking will be performed. If the value is ``False`` or
+ ``None``, CSRF checking will not be performed.
+
+ If the value provided is a string, that string will be used as the 'check
+ name'. If the value provided is ``True``, ``csrf_token`` will be used as
+ the check name.
+
+ If CSRF checking is performed, the checked value will be the value of
+ ``request.params[check_name]``. This value will be compared against the
+ value of ``request.session.get_csrf_token()``, and the check will pass if
+ these two values are the same. If the check passes, the associated view
+ will be permitted to execute. If the check fails, the associated view
+ will not be permitted to execute.
+
+ Note that using this feature requires a :term:`session factory` to have
+ been configured.
+
+ .. versionadded:: 1.4a2
+
+``physical_path``
+ If specified, this value should be a string or a tuple representing the
+ :term:`physical path` of the context found via traversal for this predicate
+ to match as true. For example: ``physical_path='/'`` or
+ ``physical_path='/a/b/c'`` or ``physical_path=('', 'a', 'b', 'c')``. This is
+ not a path prefix match or a regex, it's a whole-path match. It's useful
+ when you want to always potentially show a view when some object is traversed
+ to, but you can't be sure about what kind of object it will be, so you can't
+ use the ``context`` predicate. The individual path elements inbetween slash
+ characters or in tuple elements should be the Unicode representation of the
+ name of the resource and should not be encoded in any way.
+
+ .. versionadded:: 1.4a3
+
+``effective_principals``
+
+ If specified, this value should be a :term:`principal` identifier or a
+ sequence of principal identifiers. If the
+ :func:`pyramid.security.effective_principals` method indicates that every
+ principal named in the argument list is present in the current request, this
+ predicate will return True; otherwise it will return False. For example:
+ ``effective_principals=pyramid.security.Authenticated`` or
+ ``effective_principals=('fred', 'group:admins')``.
+
+ .. versionadded:: 1.4a4
+
``custom_predicates``
If ``custom_predicates`` is specified, it must be a sequence of references
to custom predicate callables. Use custom predicates when no set of
@@ -407,6 +456,15 @@ configured view.
If ``custom_predicates`` is not specified, no custom predicates are
used.
+``predicates``
+ Pass a key/value pair here to use a third-party predicate registered via
+ :meth:`pyramid.config.Configurator.add_view_predicate`. More than one
+ key/value pair can be used at the same time. See
+ :ref:`view_and_route_predicates` for more information about third-party
+ predicates.
+
+ .. versionadded:: 1.4a1
+
.. index::
single: view_config decorator
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index 9e41464a6..07d018127 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -338,6 +338,16 @@ exception views which have a name will be ignored.
Exception views can be configured with any view registration mechanism:
``@view_config`` decorator or imperative ``add_view`` styles.
+.. note::
+
+ Pyramid's :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.
+
.. index::
single: view http redirect
single: http redirect (from a view)
diff --git a/docs/remake b/docs/remake
index b236f2976..eb818289f 100755
--- a/docs/remake
+++ b/docs/remake
@@ -1 +1 @@
-make clean html SPHINXBUILD=../env26/bin/sphinx-build
+make clean html SPHINXBUILD=../env27/bin/sphinx-build
diff --git a/docs/tutorials/modwsgi/index.rst b/docs/tutorials/modwsgi/index.rst
index d11167344..a22f12610 100644
--- a/docs/tutorials/modwsgi/index.rst
+++ b/docs/tutorials/modwsgi/index.rst
@@ -7,12 +7,11 @@ Running a :app:`Pyramid` Application under ``mod_wsgi``
It allows :term:`WSGI` programs to be served using the Apache web
server.
-This guide will outline broad steps that can be used to get a
-:app:`Pyramid` application running under Apache via ``mod_wsgi``.
-This particular tutorial was developed under Apple's Mac OS X platform
-(Snow Leopard, on a 32-bit Mac), but the instructions should be
-largely the same for all systems, delta specific path information for
-commands and files.
+This guide will outline broad steps that can be used to get a :app:`Pyramid`
+application running under Apache via ``mod_wsgi``. This particular tutorial
+was developed under Apple's Mac OS X platform (Snow Leopard, on a 32-bit
+Mac), but the instructions should be largely the same for all systems, delta
+specific path information for commands and files.
.. note:: Unfortunately these instructions almost certainly won't work for
deploying a :app:`Pyramid` application on a Windows system using
@@ -90,12 +89,11 @@ commands and files.
`logging` module to allow logging within your application.
See :ref:`logging_config`.
-#. Make the ``pyramid.wsgi`` script executable.
-
- .. code-block:: text
-
- $ cd ~/modwsgi/env
- $ chmod 755 pyramid.wsgi
+ There is no need to make the ``pyramid.wsgi`` script executable.
+ However, you'll need to make sure that *two* users have access to change
+ into the ``~/modwsgi/env`` directory: your current user (mine is
+ ``chrism`` and the user that Apache will run as often named ``apache`` or
+ ``httpd``). Make sure both of these users can "cd" into that directory.
#. Edit your Apache configuration and add some stuff. I happened to
create a file named ``/etc/apache2/other/modwsgi.conf`` on my own
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css b/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/authorization/tutorial/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/views.py b/docs/tutorials/wiki/src/authorization/tutorial/views.py
index 21f12b31d..50485d279 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/views.py
+++ b/docs/tutorials/wiki/src/authorization/tutorial/views.py
@@ -51,17 +51,17 @@ def view_page(context, request):
renderer='templates/edit.pt',
permission='edit')
def add_page(context, request):
- name = request.subpath[0]
+ pagename = request.subpath[0]
if 'form.submitted' in request.params:
body = request.params['body']
page = Page(body)
- page.__name__ = name
+ page.__name__ = pagename
page.__parent__ = context
- context[name] = page
+ context[pagename] = page
return HTTPFound(location = request.resource_url(page))
- save_url = request.resource_url(context, 'add_page', name)
+ save_url = request.resource_url(context, 'add_page', pagename)
page = Page('')
- page.__name__ = name
+ page.__name__ = pagename
page.__parent__ = context
return dict(page = page, save_url = save_url,
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/tutorials/wiki/src/models/tutorial/static/pylons.css b/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
index 7e6ec739d..4b1c017cd 100644
--- a/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/models/tutorial/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-snall,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css b/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/tests/tutorial/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/tutorials/wiki/src/tests/tutorial/views.py b/docs/tutorials/wiki/src/tests/tutorial/views.py
index 21f12b31d..50485d279 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/views.py
+++ b/docs/tutorials/wiki/src/tests/tutorial/views.py
@@ -51,17 +51,17 @@ def view_page(context, request):
renderer='templates/edit.pt',
permission='edit')
def add_page(context, request):
- name = request.subpath[0]
+ pagename = request.subpath[0]
if 'form.submitted' in request.params:
body = request.params['body']
page = Page(body)
- page.__name__ = name
+ page.__name__ = pagename
page.__parent__ = context
- context[name] = page
+ context[pagename] = page
return HTTPFound(location = request.resource_url(page))
- save_url = request.resource_url(context, 'add_page', name)
+ save_url = request.resource_url(context, 'add_page', pagename)
page = Page('')
- page.__name__ = name
+ page.__name__ = pagename
page.__parent__ = context
return dict(page = page, save_url = save_url,
diff --git a/docs/tutorials/wiki/src/views/tutorial/static/pylons.css b/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki/src/views/tutorial/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/tutorials/wiki/src/views/tutorial/views.py b/docs/tutorials/wiki/src/views/tutorial/views.py
index 016f5b6bb..b0c15297f 100644
--- a/docs/tutorials/wiki/src/views/tutorial/views.py
+++ b/docs/tutorials/wiki/src/views/tutorial/views.py
@@ -35,17 +35,17 @@ def view_page(context, request):
@view_config(name='add_page', context='.models.Wiki',
renderer='templates/edit.pt')
def add_page(context, request):
- name = request.subpath[0]
+ pagename = request.subpath[0]
if 'form.submitted' in request.params:
body = request.params['body']
page = Page(body)
- page.__name__ = name
+ page.__name__ = pagename
page.__parent__ = context
- context[name] = page
+ context[pagename] = page
return HTTPFound(location = request.resource_url(page))
- save_url = request.resource_url(context, 'add_page', name)
+ save_url = request.resource_url(context, 'add_page', pagename)
page = Page('')
- page.__name__ = name
+ page.__name__ = pagename
page.__parent__ = context
return dict(page = page, save_url = save_url)
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index d7bd24a53..6b2d44410 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -112,7 +112,7 @@ parameter to our :term:`Configurator` constructor, that points to
the class we created above:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 19-20
+ :lines: 23-24
:linenos:
:emphasize-lines: 2
:language: python
@@ -144,7 +144,7 @@ add these import statements:
Now add those policies to the configuration:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 16-22
+ :lines: 20-26
:linenos:
:emphasize-lines: 1-3,6-7
:language: python
@@ -206,7 +206,7 @@ Go back to ``tutorial/tutorial/__init__.py`` and add these two
routes:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 25-26
+ :lines: 29-30
:linenos:
:language: python
@@ -333,7 +333,7 @@ when we're done:
.. literalinclude:: src/authorization/tutorial/__init__.py
:linenos:
- :emphasize-lines: 2-3,7,16-18,20-22,25-26
+ :emphasize-lines: 2-3,7,23-24,20-26,29-30
:language: python
(Only the highlighted lines need to be added.)
diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst
index b3184c4fc..dbd130c36 100644
--- a/docs/tutorials/wiki2/basiclayout.rst
+++ b/docs/tutorials/wiki2/basiclayout.rst
@@ -51,21 +51,33 @@ The main function first creates a SQLAlchemy database engine using
(something like ``sqlite://``):
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 9
+ :lines: 12
:linenos:
:language: py
-``main`` then initializes our SQL database using SQLAlchemy, passing it the
+``main`` then initializes our SQLAlchemy session object, passing it the
engine:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 10
+ :lines: 13
+ :language: py
+
+``main`` subsequently initializes our SQLAlchemy declarative Base object,
+assigning the engine we created to the ``bind`` attribute of it's
+``metadata`` object. This allows table definitions done imperatively
+(instead of declaratively, via a class statement) to work. We won't use any
+such tables in our application, but if you add one later, long after you've
+forgotten about this tutorial, you won't be left scratching your head when it
+doesn't work.
+
+ .. literalinclude:: src/basiclayout/tutorial/__init__.py
+ :lines: 14
:language: py
The next step of ``main`` is to construct a :term:`Configurator` object:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 11
+ :lines: 15
:language: py
``settings`` is passed to the Configurator as a keyword argument with the
@@ -78,7 +90,7 @@ deployment-related values such as ``pyramid.reload_templates``,
two arguments: ``static`` (the name), and ``static`` (the path):
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 12
+ :lines: 16
:language: py
This registers a static resource view which will match any URL that starts
@@ -96,7 +108,7 @@ via the :meth:`pyramid.config.Configurator.add_route` method that will be
used when the URL is ``/``:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 13
+ :lines: 17
:language: py
Since this route has a ``pattern`` equalling ``/`` it is the route that will
@@ -109,7 +121,7 @@ view configuration will be registered, which will allow one of our
application URLs to be mapped to some code.
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 14
+ :lines: 18
:language: py
Finally, ``main`` is finished configuring things, so it uses the
@@ -117,7 +129,7 @@ Finally, ``main`` is finished configuring things, so it uses the
:term:`WSGI` application:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 15
+ :lines: 19
:language: py
View Declarations via ``views.py``
diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst
index 24ac4338d..a54996e3c 100644
--- a/docs/tutorials/wiki2/definingviews.rst
+++ b/docs/tutorials/wiki2/definingviews.rst
@@ -342,7 +342,7 @@ something like:
.. literalinclude:: src/views/tutorial/__init__.py
:linenos:
:language: python
- :emphasize-lines: 13-16
+ :emphasize-lines: 17-20
(The highlighted lines are the ones that need to be added or edited.)
diff --git a/docs/tutorials/wiki2/design.rst b/docs/tutorials/wiki2/design.rst
index deaf32ef6..6e41e00aa 100644
--- a/docs/tutorials/wiki2/design.rst
+++ b/docs/tutorials/wiki2/design.rst
@@ -115,7 +115,8 @@ listed in the following table:
| | redirect to | | | |
| | /PageName | | | |
+----------------------+-----------------------+-------------+------------+------------+
-| /login | Display login form. | login | login.pt | |
+| /login | Display login form, | login | login.pt | |
+| | Forbidden [3]_ | | | |
| | | | | |
| | If the form was | | | |
| | submitted, | | | |
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
index 7e290a1e1..8922a3cc0 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
@@ -6,13 +6,17 @@ from sqlalchemy import engine_from_config
from tutorial.security import groupfinder
-from .models import DBSession
+from .models import (
+ DBSession,
+ Base,
+ )
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views.py b/docs/tutorials/wiki2/src/authorization/tutorial/views.py
index c7670b049..0d085b0e2 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/views.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/views.py
@@ -60,14 +60,14 @@ def view_page(request):
@view_config(route_name='add_page', renderer='templates/edit.pt',
permission='edit')
def add_page(request):
- name = request.matchdict['pagename']
+ pagename = request.matchdict['pagename']
if 'form.submitted' in request.params:
body = request.params['body']
- page = Page(name, body)
+ page = Page(pagename, body)
DBSession.add(page)
return HTTPFound(location = request.route_url('view_page',
- pagename=name))
- save_url = request.route_url('add_page', pagename=name)
+ pagename=pagename))
+ save_url = request.route_url('add_page', pagename=pagename)
page = Page('', '')
return dict(page=page, save_url=save_url,
logged_in=authenticated_userid(request))
@@ -75,16 +75,16 @@ def add_page(request):
@view_config(route_name='edit_page', renderer='templates/edit.pt',
permission='edit')
def edit_page(request):
- name = request.matchdict['pagename']
- page = DBSession.query(Page).filter_by(name=name).one()
+ pagename = request.matchdict['pagename']
+ page = DBSession.query(Page).filter_by(name=pagename).one()
if 'form.submitted' in request.params:
page.data = request.params['body']
DBSession.add(page)
return HTTPFound(location = request.route_url('view_page',
- pagename=name))
+ pagename=pagename))
return dict(
page=page,
- save_url = request.route_url('edit_page', pagename=name),
+ save_url = request.route_url('edit_page', pagename=pagename),
logged_in=authenticated_userid(request),
)
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
index 253341563..e39f619ed 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
@@ -1,13 +1,17 @@
from pyramid.config import Configurator
from sqlalchemy import engine_from_config
-from .models import DBSession
+from .models import (
+ DBSession,
+ Base,
+ )
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
config = Configurator(settings=settings)
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
index 253341563..e39f619ed 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
@@ -1,13 +1,17 @@
from pyramid.config import Configurator
from sqlalchemy import engine_from_config
-from .models import DBSession
+from .models import (
+ DBSession,
+ Base,
+ )
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
config = Configurator(settings=settings)
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/models/tutorial/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
index 7e290a1e1..8922a3cc0 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
@@ -6,13 +6,17 @@ from sqlalchemy import engine_from_config
from tutorial.security import groupfinder
-from .models import DBSession
+from .models import (
+ DBSession,
+ Base,
+ )
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/tests/tutorial/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views.py b/docs/tutorials/wiki2/src/tests/tutorial/views.py
index f2a33af1e..42ac0eb7f 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/views.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/views.py
@@ -61,15 +61,15 @@ def view_page(request):
@view_config(route_name='add_page', renderer='templates/edit.pt',
permission='edit')
def add_page(request):
- name = request.matchdict['pagename']
+ pagename = request.matchdict['pagename']
if 'form.submitted' in request.params:
session = DBSession()
body = request.params['body']
- page = Page(name, body)
+ page = Page(pagename, body)
session.add(page)
return HTTPFound(location = request.route_url('view_page',
- pagename=name))
- save_url = request.route_url('add_page', pagename=name)
+ pagename=pagename))
+ save_url = request.route_url('add_page', pagename=pagename)
page = Page('', '')
return dict(page=page, save_url=save_url,
logged_in=authenticated_userid(request))
@@ -77,17 +77,17 @@ def add_page(request):
@view_config(route_name='edit_page', renderer='templates/edit.pt',
permission='edit')
def edit_page(request):
- name = request.matchdict['pagename']
+ pagename = request.matchdict['pagename']
session = DBSession()
- page = session.query(Page).filter_by(name=name).one()
+ page = session.query(Page).filter_by(name=pagename).one()
if 'form.submitted' in request.params:
page.data = request.params['body']
session.add(page)
return HTTPFound(location = request.route_url('view_page',
- pagename=name))
+ pagename=pagename))
return dict(
page=page,
- save_url = request.route_url('edit_page', pagename=name),
+ save_url = request.route_url('edit_page', pagename=pagename),
logged_in=authenticated_userid(request),
)
diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
index b30d593cf..810e92f75 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
@@ -1,13 +1,17 @@
from pyramid.config import Configurator
from sqlalchemy import engine_from_config
-from .models import DBSession
+from .models import (
+ DBSession,
+ Base,
+ )
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
config = Configurator(settings=settings)
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('view_wiki', '/')
diff --git a/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css b/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
+++ b/docs/tutorials/wiki2/src/views/tutorial/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/tutorials/wiki2/src/views/tutorial/views.py b/docs/tutorials/wiki2/src/views/tutorial/views.py
index c2a94a96b..5a9c75a61 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/views.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/views.py
@@ -44,27 +44,27 @@ def view_page(request):
@view_config(route_name='add_page', renderer='templates/edit.pt')
def add_page(request):
- name = request.matchdict['pagename']
+ pagename = request.matchdict['pagename']
if 'form.submitted' in request.params:
body = request.params['body']
- page = Page(name, body)
+ page = Page(pagename, body)
DBSession.add(page)
return HTTPFound(location = request.route_url('view_page',
- pagename=name))
- save_url = request.route_url('add_page', pagename=name)
+ pagename=pagename))
+ save_url = request.route_url('add_page', pagename=pagename)
page = Page('', '')
return dict(page=page, save_url=save_url)
@view_config(route_name='edit_page', renderer='templates/edit.pt')
def edit_page(request):
- name = request.matchdict['pagename']
- page = DBSession.query(Page).filter_by(name=name).one()
+ pagename = request.matchdict['pagename']
+ page = DBSession.query(Page).filter_by(name=pagename).one()
if 'form.submitted' in request.params:
page.data = request.params['body']
DBSession.add(page)
return HTTPFound(location = request.route_url('view_page',
- pagename=name))
+ pagename=pagename))
return dict(
page=page,
- save_url = request.route_url('edit_page', pagename=name),
+ save_url = request.route_url('edit_page', pagename=pagename),
)
diff --git a/docs/whatsnew-1.4.rst b/docs/whatsnew-1.4.rst
new file mode 100644
index 000000000..59e1f7a96
--- /dev/null
+++ b/docs/whatsnew-1.4.rst
@@ -0,0 +1,310 @@
+What's New In Pyramid 1.4
+=========================
+
+This article explains the new features in :app:`Pyramid` version 1.4 as
+compared to its predecessor, :app:`Pyramid` 1.3. It also documents backwards
+incompatibilities between the two versions and deprecations added to
+:app:`Pyramid` 1.4, as well as software dependency changes and notable
+documentation additions.
+
+Major Feature Additions
+-----------------------
+
+The major feature additions in Pyramid 1.4 follow.
+
+Third-Party Predicates
+~~~~~~~~~~~~~~~~~~~~~~~
+
+- Third-party custom view, route, and subscriber predicates can now be added
+ for use by view authors via
+ :meth:`pyramid.config.Configurator.add_view_predicate`,
+ :meth:`pyramid.config.Configurator.add_route_predicate` and
+ :meth:`pyramid.config.Configurator.add_subscriber_predicate`. So, for
+ example, doing this::
+
+ config.add_view_predicate('abc', my.package.ABCPredicate)
+
+ Might allow a view author to do this in an application that configured that
+ predicate::
+
+ @view_config(abc=1)
+
+ Similar features exist for :meth:`pyramid.config.Configurator.add_route`,
+ and :meth:`pyramid.config.Configurator.add_subscriber`. See
+ :ref:`registering_thirdparty_predicates` for more information.
+
+Easy Custom JSON Serialization
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Views can now return custom objects which will be serialized to JSON by a
+ JSON renderer by defining a ``__json__`` method on the object's class. This
+ method should return values natively serializable by ``json.dumps`` (such
+ as ints, lists, dictionaries, strings, and so forth). See
+ :ref:`json_serializing_custom_objects` for more information. The JSON
+ renderer now also allows for the definition of custom type adapters to
+ convert unknown objects to JSON serializations, in case you can't add a
+ ``__json__`` method to returned objects.
+
+Partial Mako and Chameleon Template Renderings
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- The Mako renderer now supports using a def name in an asset spec. When the
+ def name is present in the asset spec, the system will render the template
+ named def within the template instead of rendering the entire template. An
+ example asset spec which names a def is
+ ``package:path/to/template#defname.mako``. This will render the def named
+ ``defname`` inside the ``template.mako`` template instead of rendering the
+ entire template. The old way of returning a tuple in the form
+ ``('defname', {})`` from the view is supported for backward compatibility.
+
+- The Chameleon ZPT renderer now supports using a macro name in an asset
+ spec. When the macro name is present in the asset spec, the system will
+ render the macro listed as a ``define-macro`` and return the result instead
+ of rendering the entire template. An example asset spec:
+ ``package:path/to/template#macroname.pt``. This will render the macro
+ defined as ``macroname`` within the ``template.pt`` template instead of the
+ entire template.
+
+Subrequest Support
+~~~~~~~~~~~~~~~~~~
+
+- Developers may invoke a subrequest by using the
+ :meth:`pyramid.request.Request.invoke_subrequest` API. This allows a
+ developer to obtain a response from one view callable by issuing a subrequest
+ from within a different view callable. See :ref:`subrequest_chapter` for
+ more information.
+
+Minor Feature Additions
+-----------------------
+
+- :meth:`pyramid.config.Configurator.add_directive` now accepts arbitrary
+ callables like partials or objects implementing ``__call__`` which don't
+ have ``__name__`` and ``__doc__`` attributes. See
+ https://github.com/Pylons/pyramid/issues/621 and
+ https://github.com/Pylons/pyramid/pull/647.
+
+- As of this release, the ``request_method`` view/route predicate, when used,
+ will also imply that ``HEAD`` is implied when you use ``GET``. For
+ example, using ``@view_config(request_method='GET')`` is equivalent to
+ using ``@view_config(request_method=('GET', 'HEAD'))``. Using
+ ``@view_config(request_method=('GET', 'POST')`` is equivalent to using
+ ``@view_config(request_method=('GET', 'HEAD', 'POST')``. This is because
+ HEAD is a variant of GET that omits the body, and WebOb has special support
+ to return an empty body when a HEAD is used.
+
+- :meth:`pyramid.config.Configurator.add_request_method` has been introduced
+ to support extending request objects with arbitrary callables. This method
+ expands on the now documentation-deprecated
+ :meth:`pyramid.config.Configurator.set_request_property` by supporting
+ methods as well as properties. This method also causes less code to be
+ executed at request construction time than
+ :meth:`~pyramid.config.Configurator.set_request_property`.
+
+- The static view machinery now raises rather than returns
+ :class:`pyramid.httpexceptions.HTTPNotFound` and
+ :class:`pyramid.httpexceptions.HTTPMovedPermanently` exceptions, so these can
+ be caught by the notfound view (and other exception views).
+
+- When there is a predicate mismatch exception (seen when no view matches for
+ a given request due to predicates not working), the exception now contains
+ a textual description of the predicate which didn't match.
+
+- An :meth:`pyramid.config.Configurator.add_permission` directive method was
+ added to the Configurator. This directive registers a free-standing
+ permission introspectable into the Pyramid introspection system.
+ Frameworks built atop Pyramid can thus use the ``permissions``
+ introspectable category data to build a comprehensive list of permissions
+ supported by a running system. Before this method was added, permissions
+ were already registered in this introspectable category as a side effect of
+ naming them in an :meth:`pyramid.config.Configurator.add_view` call, this
+ method just makes it possible to arrange for a permission to be put into
+ the ``permissions`` introspectable category without naming it along with an
+ associated view. Here's an example of usage of ``add_permission``::
+
+ config = Configurator()
+ config.add_permission('view')
+
+- The :func:`pyramid.session.UnencryptedCookieSessionFactoryConfig` function
+ now accepts ``signed_serialize`` and ``signed_deserialize`` hooks which may
+ be used to influence how the sessions are marshalled (by default this is
+ done with HMAC+pickle).
+
+- :class:`pyramid.testing.DummyRequest` now supports methods supplied by the
+ ``pyramid.util.InstancePropertyMixin`` class such as ``set_property``.
+
+- Request properties and methods added via
+ :meth:`pyramid.config.Configurator.add_request_method` or
+ :meth:`pyramid.config.Configurator.set_request_property` are now available to
+ tweens.
+
+- Request properties and methods added via
+ :meth:`pyramid.config.Configurator.add_request_method` or
+ :meth:`pyramid.config.Configurator.set_request_property` are now available
+ in the request object returned from :func:`pyramid.paster.bootstrap`.
+
+- ``request.context`` of environment request during
+ :func:`pyramid.paster.bootstrap` is now the root object if a context isn't
+ already set on a provided request.
+
+- :class:`pyramid.decorator.reify` is now an API, and was added to
+ the API documentation.
+
+- Added the :func:`pyramid.testing.testConfig` context manager, which can be
+ used to generate a configurator in a test, e.g. ``with
+ testing.testConfig(...):``.
+
+- A new :func:`pyramid.session.check_csrf_token` convenience API function was
+ added.
+
+- A ``check_csrf`` view predicate was added. For example, you can now do
+ ``config.add_view(someview, check_csrf=True)``. When the predicate is
+ checked, if the ``csrf_token`` value in ``request.params`` matches the csrf
+ token in the request's session, the view will be permitted to execute.
+ Otherwise, it will not be permitted to execute.
+
+- Add ``Base.metadata.bind = engine`` to ``alchemy`` scaffold, so that tables
+ defined imperatively will work.
+
+- Comments with references to documentation sections placed in scaffold
+ ``.ini`` files.
+
+- Allow multiple values to be specified to the ``request_param`` view/route
+ predicate as a sequence. Previously only a single string value was allowed.
+ See https://github.com/Pylons/pyramid/pull/705
+
+- Added an HTTP Basic authentication policy
+ at :class:`pyramid.authentication.BasicAuthAuthenticationPolicy`.
+
+- The :meth:`pyramid.config.Configurator.testing_securitypolicy` method now
+ returns the policy object it creates.
+
+- The DummySecurityPolicy created by
+ :meth:`pyramid.config.testing_securitypolicy` now sets a ``forgotten`` value
+ on the policy (the value ``True``) when its ``forget`` method is called.
+
+
+- The DummySecurityPolicy created by
+ :meth:`pyramid.config.testing_securitypolicy` now sets a
+ ``remembered`` value on the policy, which is the value of the ``principal``
+ argument it's called with when its ``remember`` method is called.
+
+- New ``physical_path`` view predicate. If specified, this value should be a
+ string or a tuple representing the physical traversal path of the context
+ found via traversal for this predicate to match as true. For example:
+ ``physical_path='/'`` or ``physical_path='/a/b/c'`` or ``physical_path=('',
+ 'a', 'b', 'c')``. It's useful when you want to always potentially show a
+ view when some object is traversed to, but you can't be sure about what kind
+ of object it will be, so you can't use the ``context`` predicate.
+
+Backwards Incompatibilities
+---------------------------
+
+- The Pyramid router no longer adds the values ``bfg.routes.route`` or
+ ``bfg.routes.matchdict`` to the request's WSGI environment dictionary.
+ These values were docs-deprecated in ``repoze.bfg`` 1.0 (effectively seven
+ minor releases ago). If your code depended on these values, use
+ ``request.matched_route`` and ``request.matchdict`` instead.
+
+- It is no longer possible to pass an environ dictionary directly to
+ ``pyramid.traversal.ResourceTreeTraverser.__call__`` (aka
+ ``ModelGraphTraverser.__call__``). Instead, you must pass a request
+ object. Passing an environment instead of a request has generated a
+ deprecation warning since Pyramid 1.1.
+
+- Pyramid will no longer work properly if you use the
+ ``webob.request.LegacyRequest`` as a request factory. Instances of the
+ LegacyRequest class have a ``request.path_info`` which return a string.
+ This Pyramid release assumes that ``request.path_info`` will
+ unconditionally be Unicode.
+
+- The functions from ``pyramid.chameleon_zpt`` and ``pyramid.chameleon_text``
+ named ``get_renderer``, ``get_template``, ``render_template``, and
+ ``render_template_to_response`` have been removed. These have issued a
+ deprecation warning upon import since Pyramid 1.0. Use
+ :func:`pyramid.renderers.get_renderer`,
+ ``pyramid.renderers.get_renderer().implementation()``,
+ :func:`pyramid.renderers.render` or
+ :func:`pyramid.renderers.render_to_response` respectively instead of these
+ functions.
+
+- The ``pyramid.configuration`` module was removed. It had been deprecated
+ since Pyramid 1.0 and printed a deprecation warning upon its use. Use
+ :mod:`pyramid.config` instead.
+
+- The ``pyramid.paster.PyramidTemplate`` API was removed. It had been
+ deprecated since Pyramid 1.1 and issued a warning on import. If your code
+ depended on this, adjust your code to import
+ :class:`pyramid.scaffolds.PyramidTemplate` instead.
+
+- The ``pyramid.settings.get_settings()`` API was removed. It had been
+ printing a deprecation warning since Pyramid 1.0. If your code depended on
+ this API, use ``pyramid.threadlocal.get_current_registry().settings``
+ instead or use the ``settings`` attribute of the registry available from
+ the request (``request.registry.settings``).
+
+- These APIs from the ``pyramid.testing`` module were removed. They have
+ been printing deprecation warnings since Pyramid 1.0:
+
+ * ``registerDummySecurityPolicy``, use
+ :meth:`pyramid.config.Configurator.testing_securitypolicy` instead.
+
+ * ``registerResources`` (aka ``registerModels``), use
+ :meth:`pyramid.config.Configurator.testing_resources` instead.
+
+ * ``registerEventListener``, use
+ :meth:`pyramid.config.Configurator.testing_add_subscriber` instead.
+
+ * ``registerTemplateRenderer`` (aka ``registerDummyRenderer``), use
+ :meth:`pyramid.config.Configurator.testing_add_template` instead.
+
+ * ``registerView``, use :meth:`pyramid.config.Configurator.add_view` instead.
+
+ * ``registerUtility``, use
+ :meth:`pyramid.config.Configurator.registry.registerUtility` instead.
+
+ * ``registerAdapter``, use
+ :meth:`pyramid.config.Configurator.registry.registerAdapter` instead.
+
+ * ``registerSubscriber``, use
+ :meth:`pyramid.config.Configurator.add_subscriber` instead.
+
+ * ``registerRoute``, use
+ :meth:`pyramid.config.Configurator.add_route` instead.
+
+ * ``registerSettings``, use
+ :meth:`pyramid.config.Configurator.add_settings` instead.
+
+- In Pyramid 1.3 and previous, the ``__call__`` method of a Response object
+ returned by a view was invoked before any finished callbacks were executed.
+ As of this release, the ``__call__`` method of a Response object is invoked
+ *after* finished callbacks are executed. This is in support of the
+ :meth:`pyramid.request.Request.invoke_subrequest` feature.
+
+Deprecations
+------------
+
+- The :meth:`pyramid.config.Configurator.set_request_property` directive has
+ been documentation-deprecated. The method remains usable but the more
+ featureful :meth:`pyramid.config.Configurator.add_request_method` should be
+ used in its place (it has all of the same capabilities but can also extend
+ the request object with methods).
+
+Documentation Enhancements
+--------------------------
+
+- Added an :ref:`upgrading_chapter` chapter to the narrative documentation.
+ It describes how to cope with deprecations and removals of Pyramid APIs and
+ how to show Pyramid-generated deprecation warnings while running tests and
+ while running a server.
+
+- Added a :ref:`subrequest_chapter` chapter to the narrative documentation.
+
+- Many cleanups and improvements to narrative and API docs.
+
+Dependency Changes
+------------------
+
+- Pyramid now requires WebOb 1.2b3+ (the prior Pyramid release only relied on
+ 1.2dev+). This is to ensure that we obtain a version of WebOb that returns
+ ``request.path_info`` as text.
+
diff --git a/pyramid/__init__.py b/pyramid/__init__.py
index 473d5e1c6..5bb534f79 100644
--- a/pyramid/__init__.py
+++ b/pyramid/__init__.py
@@ -1,5 +1 @@
-from pyramid.request import Request
-from pyramid.response import Response
-Response.RequestClass = Request
-Request.ResponseClass = Response
-del Request, Response
+# package
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index 730312144..7e7ee4dfb 100644
--- a/pyramid/authentication.py
+++ b/pyramid/authentication.py
@@ -1,3 +1,4 @@
+import binascii
from codecs import utf_8_decode
from codecs import utf_8_encode
import hashlib
@@ -47,7 +48,21 @@ class CallbackAuthenticationPolicy(object):
methodname = classname + '.' + methodname
logger.debug(methodname + ': ' + msg)
+ def _clean_principal(self, princid):
+ if princid in (Authenticated, Everyone):
+ princid = None
+ return princid
+
def authenticated_userid(self, request):
+ """ Return the authenticated userid or ``None``.
+
+ If no callback is registered, this will be the same as
+ ``unauthenticated_userid``.
+
+ If a ``callback`` is registered, this will return the userid if
+ and only if the callback returns a value that is not ``None``.
+
+ """
debug = self.debug
userid = self.unauthenticated_userid(request)
if userid is None:
@@ -56,6 +71,14 @@ class CallbackAuthenticationPolicy(object):
'authenticated_userid',
request)
return None
+ if self._clean_principal(userid) is None:
+ debug and self._log(
+ ('use of userid %r is disallowed by any built-in Pyramid '
+ 'security policy, returning None' % userid),
+ 'authenticated_userid' ,
+ request)
+ return None
+
if self.callback is None:
debug and self._log(
'there was no groupfinder callback; returning %r' % (userid,),
@@ -78,9 +101,32 @@ class CallbackAuthenticationPolicy(object):
)
def effective_principals(self, request):
+ """ A list of effective principals derived from request.
+
+ This will return a list of principals including, at least,
+ :data:`pyramid.security.Everyone`. If there is no authenticated
+ userid, or the ``callback`` returns ``None``, this will be the
+ only principal:
+
+ .. code-block:: python
+
+ return [Everyone]
+
+ If the ``callback`` does not return ``None`` and an authenticated
+ userid is found, then the principals will include
+ :data:`pyramid.security.Authenticated`, the ``authenticated_userid``
+ and the list of principals returned by the ``callback``:
+
+ .. code-block:: python
+
+ extra_principals = callback(userid, request)
+ return [Everyone, Authenticated, userid] + extra_principals
+
+ """
debug = self.debug
effective_principals = [Everyone]
userid = self.unauthenticated_userid(request)
+
if userid is None:
debug and self._log(
'unauthenticated_userid returned %r; returning %r' % (
@@ -89,6 +135,16 @@ class CallbackAuthenticationPolicy(object):
request
)
return effective_principals
+
+ if self._clean_principal(userid) is None:
+ debug and self._log(
+ ('unauthenticated_userid returned disallowed %r; returning %r '
+ 'as if it was None' % (userid, effective_principals)),
+ 'effective_principals',
+ request
+ )
+ return effective_principals
+
if self.callback is None:
debug and self._log(
'groupfinder callback is None, so groups is []',
@@ -101,6 +157,7 @@ class CallbackAuthenticationPolicy(object):
'groupfinder callback returned %r as groups' % (groups,),
'effective_principals',
request)
+
if groups is None: # is None!
debug and self._log(
'returning effective principals: %r' % (
@@ -163,39 +220,120 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
return identifier
def authenticated_userid(self, request):
+ """ Return the authenticated userid or ``None``.
+
+ If no callback is registered, this will be the same as
+ ``unauthenticated_userid``.
+
+ If a ``callback`` is registered, this will return the userid if
+ and only if the callback returns a value that is not ``None``.
+
+ """
identity = self._get_identity(request)
+
if identity is None:
+ self.debug and self._log(
+ 'repoze.who identity is None, returning None',
+ 'authenticated_userid',
+ request)
return None
+
+ userid = identity['repoze.who.userid']
+
+ if userid is None:
+ self.debug and self._log(
+ 'repoze.who.userid is None, returning None' % userid,
+ 'authenticated_userid',
+ request)
+ return None
+
+ if self._clean_principal(userid) is None:
+ self.debug and self._log(
+ ('use of userid %r is disallowed by any built-in Pyramid '
+ 'security policy, returning None' % userid),
+ 'authenticated_userid',
+ request)
+ return None
+
if self.callback is None:
- return identity['repoze.who.userid']
+ return userid
+
if self.callback(identity, request) is not None: # is not None!
- return identity['repoze.who.userid']
+ return userid
def unauthenticated_userid(self, request):
+ """ Return the ``repoze.who.userid`` key from the detected identity."""
identity = self._get_identity(request)
if identity is None:
return None
return identity['repoze.who.userid']
def effective_principals(self, request):
+ """ A list of effective principals derived from the identity.
+
+ This will return a list of principals including, at least,
+ :data:`pyramid.security.Everyone`. If there is no identity, or
+ the ``callback`` returns ``None``, this will be the only principal.
+
+ If the ``callback`` does not return ``None`` and an identity is
+ found, then the principals will include
+ :data:`pyramid.security.Authenticated`, the ``authenticated_userid``
+ and the list of principals returned by the ``callback``.
+
+ """
effective_principals = [Everyone]
identity = self._get_identity(request)
+
if identity is None:
+ self.debug and self._log(
+ ('repoze.who identity was None; returning %r' %
+ effective_principals),
+ 'effective_principals',
+ request
+ )
return effective_principals
+
if self.callback is None:
groups = []
else:
groups = self.callback(identity, request)
+
if groups is None: # is None!
+ self.debug and self._log(
+ ('security policy groups callback returned None; returning %r' %
+ effective_principals),
+ 'effective_principals',
+ request
+ )
return effective_principals
+
userid = identity['repoze.who.userid']
+
+ if userid is None:
+ self.debug and self._log(
+ ('repoze.who.userid was None; returning %r' %
+ effective_principals),
+ 'effective_principals',
+ request
+ )
+ return effective_principals
+
+ if self._clean_principal(userid) is None:
+ self.debug and self._log(
+ ('unauthenticated_userid returned disallowed %r; returning %r '
+ 'as if it was None' % (userid, effective_principals)),
+ 'effective_principals',
+ request
+ )
+ return effective_principals
+
effective_principals.append(Authenticated)
effective_principals.append(userid)
effective_principals.extend(groups)
-
return effective_principals
def remember(self, request, principal, **kw):
+ """ Store the ``principal`` as ``repoze.who.userid``."""
identifier = self._get_identifier(request)
if identifier is None:
return []
@@ -204,6 +342,12 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
return identifier.remember(environ, identity)
def forget(self, request):
+ """ Forget the current authenticated user.
+
+ Return headers that, if included in a response, will delete the
+ cookie responsible for tracking the current user.
+
+ """
identifier = self._get_identifier(request)
if identifier is None:
return []
@@ -247,12 +391,19 @@ class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
self.debug = debug
def unauthenticated_userid(self, request):
+ """ The ``REMOTE_USER`` value found within the ``environ``."""
return request.environ.get(self.environ_key)
def remember(self, request, principal, **kw):
+ """ A no-op. The ``REMOTE_USER`` does not provide a protocol for
+ remembering the user. This will be application-specific and can
+ be done somewhere else or in a subclass."""
return []
def forget(self, request):
+ """ A no-op. The ``REMOTE_USER`` does not provide a protocol for
+ forgetting the user. This will be application-specific and can
+ be done somewhere else or in a subclass."""
return []
class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
@@ -330,13 +481,13 @@ class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
Optional.
``path``
-
+
Default: ``/``. The path for which the auth_tkt cookie is valid.
May be desirable if the application only serves part of a domain.
Optional.
-
+
``http_only``
-
+
Default: ``False``. Hide cookie from JavaScript by setting the
HttpOnly flag. Not honored by all browsers.
Optional.
@@ -390,16 +541,23 @@ class BaseAuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
self.debug = debug
def unauthenticated_userid(self, request):
+ """ The userid key within the auth_tkt cookie."""
result = self.cookie.identify(request)
if result:
return result['userid']
def remember(self, request, principal, **kw):
""" Accepts the following kw args: ``max_age=<int-seconds>,
- ``tokens=<sequence-of-ascii-strings>``"""
+ ``tokens=<sequence-of-ascii-strings>``.
+
+ Return a list of headers which will set appropriate cookies on
+ the response.
+
+ """
return self.cookie.remember(request, principal, **kw)
def forget(self, request):
+ """ A list of headers which will delete appropriate cookies."""
return self.cookie.forget(request)
@implementer(IAuthenticationPolicy)
@@ -588,7 +746,7 @@ class AuthTktCookieHelper(object):
text_type: ('b64unicode', lambda x: b64encode(utf_8_encode(x)[0])),
binary_type: ('b64str', lambda x: b64encode(x)),
}
-
+
def __init__(self, secret, cookie_name='auth_tkt', secure=False,
include_ip=False, timeout=None, reissue_time=None,
max_age=None, http_only=False, path="/", wild_domain=True, hashalg='md5'):
@@ -668,7 +826,7 @@ class AuthTktCookieHelper(object):
remote_addr = environ['REMOTE_ADDR']
else:
remote_addr = '0.0.0.0'
-
+
try:
timestamp, userid, tokens, user_data = self.parse_ticket(
self.secret, cookie, remote_addr, self.hashalg)
@@ -677,7 +835,7 @@ class AuthTktCookieHelper(object):
now = self.now # service tests
- if now is None:
+ if now is None:
now = time_mod.time()
if self.timeout and ( (timestamp + self.timeout) < now ):
@@ -725,7 +883,7 @@ class AuthTktCookieHelper(object):
environ = request.environ
request._authtkt_reissue_revoked = True
return self._get_cookies(environ, '', max_age=EXPIRE)
-
+
def remember(self, request, userid, max_age=None, tokens=()):
""" Return a set of Set-Cookie headers; when set into a response,
these headers will represent a valid authentication ticket.
@@ -820,7 +978,7 @@ class SessionAuthenticationPolicy(CallbackAuthenticationPolicy):
Pyramid debug logger about the results of various authentication
steps. The output from debugging is useful for reporting to maillist
or IRC channels when asking for support.
-
+
"""
def __init__(self, prefix='auth.', callback=None, debug=False):
@@ -843,3 +1001,101 @@ class SessionAuthenticationPolicy(CallbackAuthenticationPolicy):
def unauthenticated_userid(self, request):
return request.session.get(self.userid_key)
+
+@implementer(IAuthenticationPolicy)
+class BasicAuthAuthenticationPolicy(CallbackAuthenticationPolicy):
+ """ A :app:`Pyramid` authentication policy which uses HTTP standard basic
+ authentication protocol to authenticate users. To use this policy you will
+ need to provide a callback which checks the supplied user credentials
+ against your source of login data.
+
+ Constructor Arguments
+
+ ``check``
+
+ A callback function passed a username, password and request, in that
+ order as positional arguments. Expected to return ``None`` if the
+ userid doesn't exist or a sequence of principal identifiers (possibly
+ empty) if the user does exist.
+
+ ``realm``
+
+ Default: ``"Realm"``. The Basic Auth Realm string. Usually displayed to
+ the user by the browser in the login dialog.
+
+ ``debug``
+
+ Default: ``False``. If ``debug`` is ``True``, log messages to the
+ Pyramid debug logger about the results of various authentication
+ steps. The output from debugging is useful for reporting to maillist
+ or IRC channels when asking for support.
+
+ **Issuing a challenge**
+
+ Regular browsers will not send username/password credentials unless they
+ first receive a challenge from the server. The following recipe will
+ register a view that will send a Basic Auth challenge to the user whenever
+ there is an attempt to call a view which results in a Forbidden response::
+
+ from pyramid.httpexceptions import HTTPForbidden
+ from pyramid.httpexceptions import HTTPUnauthorized
+ from pyramid.security import forget
+ from pyramid.view import view_config
+
+ @view_config(context=HTTPForbidden)
+ def basic_challenge(request):
+ response = HTTPUnauthorized()
+ response.headers.update(forget(request))
+ return response
+ """
+ def __init__(self, check, realm='Realm', debug=False):
+ self.check = check
+ self.realm = realm
+ self.debug = debug
+
+ def unauthenticated_userid(self, request):
+ """ The userid parsed from the ``Authorization`` request header."""
+ credentials = self._get_credentials(request)
+ if credentials:
+ return credentials[0]
+
+ def remember(self, request, principal, **kw):
+ """ A no-op. Basic authentication does not provide a protocol for
+ remembering the user. Credentials are sent on every request.
+
+ """
+ return []
+
+ def forget(self, request):
+ """ Returns challenge headers. This should be attached to a response
+ to indicate that credentials are required."""
+ return [('WWW-Authenticate', 'Basic realm="%s"' % self.realm)]
+
+ def callback(self, username, request):
+ # Username arg is ignored. Unfortunately _get_credentials winds up
+ # getting called twice when authenticated_userid is called. Avoiding
+ # that, however, winds up duplicating logic from the superclass.
+ credentials = self._get_credentials(request)
+ if credentials:
+ username, password = credentials
+ return self.check(username, password, request)
+
+ def _get_credentials(self, request):
+ authorization = request.headers.get('Authorization')
+ if not authorization:
+ return None
+ try:
+ authmeth, auth = authorization.split(' ', 1)
+ except ValueError: # not enough values to unpack
+ return None
+ if authmeth.lower() != 'basic':
+ return None
+ try:
+ auth = b64decode(auth.strip()).decode('ascii')
+ except (TypeError, binascii.Error): # can't decode
+ return None
+ try:
+ username, password = auth.split(':', 1)
+ except ValueError: # not enough values to unpack
+ return None
+ return username, password
diff --git a/pyramid/chameleon_text.py b/pyramid/chameleon_text.py
index 3484b7973..8cf04bf79 100644
--- a/pyramid/chameleon_text.py
+++ b/pyramid/chameleon_text.py
@@ -1,19 +1,6 @@
-import sys
-
from zope.interface import implementer
-from pyramid.compat import reraise
-
-try:
- from chameleon.zpt.template import PageTextTemplateFile
- # prevent pyflakes complaining about a redefinition below
- PageTextTemplateFile
-except ImportError: # pragma: no cover
- exc_class, exc, tb = sys.exc_info()
- # Chameleon doesn't work on non-CPython platforms
- class PageTextTemplateFile(object):
- def __init__(self, *arg, **kw):
- reraise(ImportError, exc, tb)
+from chameleon.zpt.template import PageTextTemplateFile
from pyramid.interfaces import ITemplateRenderer
@@ -33,9 +20,6 @@ class TextTemplateRenderer(object):
@reify # avoid looking up reload_templates before manager pushed
def template(self):
- if sys.platform.startswith('java'): # pragma: no cover
- raise RuntimeError(
- 'Chameleon templates are not compatible with Jython')
return PageTextTemplateFile(self.path,
auto_reload=self.lookup.auto_reload,
debug=self.lookup.debug,
diff --git a/pyramid/chameleon_zpt.py b/pyramid/chameleon_zpt.py
index d9f4337fa..d8a8ee1be 100644
--- a/pyramid/chameleon_zpt.py
+++ b/pyramid/chameleon_zpt.py
@@ -1,21 +1,8 @@
-import sys
-
from zope.interface import implementer
-from pyramid.compat import reraise
-
-try:
- from chameleon.zpt.template import PageTemplateFile
- PageTemplateFile # prevent pyflakes complaining about a redefinition below
-except ImportError: # pragma: no cover
- exc_class, exc, tb = sys.exc_info()
- # Chameleon doesn't work on non-CPython platforms
- class PageTemplateFile(object):
- def __init__(self, *arg, **kw):
- reraise(ImportError, exc, tb)
+from chameleon.zpt.template import PageTemplateFile
from pyramid.interfaces import ITemplateRenderer
-
from pyramid.decorator import reify
from pyramid import renderers
@@ -31,13 +18,12 @@ class ZPTTemplateRenderer(object):
@reify # avoid looking up reload_templates before manager pushed
def template(self):
- if sys.platform.startswith('java'): # pragma: no cover
- raise RuntimeError(
- 'Chameleon templates are not compatible with Jython')
- tf = PageTemplateFile(self.path,
- auto_reload=self.lookup.auto_reload,
- debug=self.lookup.debug,
- translate=self.lookup.translate)
+ tf = PageTemplateFile(
+ self.path,
+ auto_reload=self.lookup.auto_reload,
+ debug=self.lookup.debug,
+ translate=self.lookup.translate
+ )
if self.macro:
# render only the portion of the template included in a
# define-macro named the value of self.macro
diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py
index c5d453654..01b1fb22e 100644
--- a/pyramid/config/factories.py
+++ b/pyramid/config/factories.py
@@ -1,11 +1,9 @@
from zope.interface import implementer
-from pyramid.compat import iteritems_
from pyramid.config.util import action_method
from pyramid.interfaces import (
IDefaultRootFactory,
- INewRequest,
IRequestFactory,
IRequestExtensions,
IRootFactory,
@@ -184,7 +182,7 @@ class FactoriesConfiguratorMixin(object):
.. warning::
- This method has been deprecated as of Pyramid 1.4.
+ This method has been docs-deprecated as of Pyramid 1.4.
:meth:`pyramid.config.Configurator.add_request_method` should be
used instead.
diff --git a/pyramid/config/predicates.py b/pyramid/config/predicates.py
index 9e0ee28c1..e31425899 100644
--- a/pyramid/config/predicates.py
+++ b/pyramid/config/predicates.py
@@ -1,17 +1,19 @@
import re
-from pyramid.compat import is_nonstr_iter
-
from pyramid.exceptions import ConfigurationError
+from pyramid.compat import is_nonstr_iter
+
from pyramid.traversal import (
find_interface,
traversal_path,
+ resource_path_tuple
)
from pyramid.urldispatch import _compile_route
-
from pyramid.util import object_description
+from pyramid.session import check_csrf_token
+from pyramid.security import effective_principals
from .util import as_sorted_tuple
@@ -62,43 +64,48 @@ class PathInfoPredicate(object):
class RequestParamPredicate(object):
def __init__(self, val, config):
- name = val
- v = None
- if '=' in name:
- name, v = name.split('=', 1)
- name, v = name.strip(), v.strip()
- if v is None:
- self._text = 'request_param %s' % (name,)
- else:
- self._text = 'request_param %s = %s' % (name, v)
- self.name = name
- self.val = v
+ val = as_sorted_tuple(val)
+ reqs = []
+ for p in val:
+ k = p
+ v = None
+ if '=' in p:
+ k, v = p.split('=', 1)
+ k, v = k.strip(), v.strip()
+ reqs.append((k, v))
+ self.val = val
+ self.reqs = reqs
def text(self):
- return self._text
+ return 'request_param %s' % ','.join(
+ ['%s=%s' % (x,y) if y else x for x, y in self.reqs]
+ )
phash = text
def __call__(self, context, request):
- if self.val is None:
- return self.name in request.params
- return request.params.get(self.name) == self.val
-
+ for k, v in self.reqs:
+ actual = request.params.get(k)
+ if actual is None:
+ return False
+ if v is not None and actual != v:
+ return False
+ return True
class HeaderPredicate(object):
def __init__(self, val, config):
name = val
v = None
if ':' in name:
- name, v = name.split(':', 1)
+ name, val_str = name.split(':', 1)
try:
- v = re.compile(v)
+ v = re.compile(val_str)
except re.error as why:
raise ConfigurationError(why.args[0])
if v is None:
self._text = 'header %s' % (name,)
else:
- self._text = 'header %s = %s' % (name, v)
+ self._text = 'header %s=%s' % (name, val_str)
self.name = name
self.val = v
@@ -154,9 +161,7 @@ class RequestTypePredicate(object):
class MatchParamPredicate(object):
def __init__(self, val, config):
- if not is_nonstr_iter(val):
- val = (val,)
- val = sorted(val)
+ val = as_sorted_tuple(val)
self.val = val
reqs = [ p.split('=', 1) for p in val ]
self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ]
@@ -226,3 +231,59 @@ class TraversePredicate(object):
# injects ``traverse`` into the matchdict. As a result, we just
# return True.
return True
+
+class CheckCSRFTokenPredicate(object):
+
+ check_csrf_token = staticmethod(check_csrf_token) # testing
+
+ def __init__(self, val, config):
+ self.val = val
+
+ def text(self):
+ return 'check_csrf = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ val = self.val
+ if val:
+ if val is True:
+ val = 'csrf_token'
+ return self.check_csrf_token(request, val, raises=False)
+ return True
+
+class PhysicalPathPredicate(object):
+ def __init__(self, val, config):
+ if is_nonstr_iter(val):
+ self.val = tuple(val)
+ else:
+ val = tuple(filter(None, val.split('/')))
+ self.val = ('',) + val
+
+ def text(self):
+ return 'physical_path = %s' % (self.val,)
+
+ phash = text
+
+ def __call__(self, context, request):
+ return resource_path_tuple(context) == self.val
+
+class EffectivePrincipalsPredicate(object):
+ def __init__(self, val, config):
+ if is_nonstr_iter(val):
+ self.val = set(val)
+ else:
+ self.val = set((val,))
+
+ def text(self):
+ return 'effective_principals = %s' % sorted(list(self.val))
+
+ phash = text
+
+ def __call__(self, context, request):
+ req_principals = effective_principals(request)
+ if is_nonstr_iter(req_principals):
+ rpset = set(req_principals)
+ if self.val.issubset(rpset):
+ return True
+ return False
diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py
index 1a7fdfac9..7c61d5912 100644
--- a/pyramid/config/routes.py
+++ b/pyramid/config/routes.py
@@ -4,7 +4,6 @@ from pyramid.interfaces import (
IRequest,
IRouteRequest,
IRoutesMapper,
- PHASE1_CONFIG,
PHASE2_CONFIG,
)
@@ -239,6 +238,19 @@ class RoutesConfiguratorMixin(object):
request, this predicate will be true. If this predicate
returns ``False``, route matching continues.
+ effective_principals
+
+ If specified, this value should be a :term:`principal` identifier or
+ a sequence of principal identifiers. If the
+ :func:`pyramid.security.effective_principals` method indicates that
+ every principal named in the argument list is present in the current
+ request, this predicate will return True; otherwise it will return
+ False. For example:
+ ``effective_principals=pyramid.security.Authenticated`` or
+ ``effective_principals=('fred', 'group:admins')``.
+
+ .. versionadded:: 1.4a4
+
custom_predicates
This value should be a sequence of references to custom
@@ -500,6 +512,7 @@ class RoutesConfiguratorMixin(object):
('request_param', p.RequestParamPredicate),
('header', p.HeaderPredicate),
('accept', p.AcceptPredicate),
+ ('effective_principals', p.EffectivePrincipalsPredicate),
('custom', p.CustomPredicate),
('traverse', p.TraversePredicate),
):
diff --git a/pyramid/config/testing.py b/pyramid/config/testing.py
index f40cf25a7..abbbffc10 100644
--- a/pyramid/config/testing.py
+++ b/pyramid/config/testing.py
@@ -19,7 +19,8 @@ from pyramid.config.util import action_method
class TestingConfiguratorMixin(object):
# testing API
def testing_securitypolicy(self, userid=None, groupids=(),
- permissive=True):
+ permissive=True, remember_result=None,
+ forget_result=None):
"""Unit/integration testing helper: Registers a pair of faux
:app:`Pyramid` security policies: a :term:`authentication
policy` and a :term:`authorization policy`.
@@ -31,6 +32,24 @@ class TestingConfiguratorMixin(object):
nonpermissive :term:`authorization policy` is registered; this
policy denies all access.
+ ``remember_result``, if provided, should be the result returned by
+ the ``remember`` method of the faux authentication policy. If it is
+ not provided (or it is provided, and is ``None``), the default value
+ ``[]`` (the empty list) will be returned by ``remember``.
+
+ .. note::
+
+ ``remember_result`` is new as of Pyramid 1.4.
+
+ ``forget_result``, if provided, should be the result returned by
+ the ``forget`` method of the faux authentication policy. If it is
+ not provided (or it is provided, and is ``None``), the default value
+ ``[]`` (the empty list) will be returned by ``forget``.
+
+ .. note::
+
+ ``forget_result`` is new as of Pyramid 1.4.
+
The behavior of the registered :term:`authentication policy`
depends on the values provided for the ``userid`` and
``groupids`` argument. The authentication policy will return
@@ -47,9 +66,12 @@ class TestingConfiguratorMixin(object):
:func:`pyramid.security.principals_allowed_by_permission`.
"""
from pyramid.testing import DummySecurityPolicy
- policy = DummySecurityPolicy(userid, groupids, permissive)
+ policy = DummySecurityPolicy(
+ userid, groupids, permissive, remember_result, forget_result
+ )
self.registry.registerUtility(policy, IAuthorizationPolicy)
self.registry.registerUtility(policy, IAuthenticationPolicy)
+ return policy
def testing_resources(self, resources):
"""Unit/integration testing helper: registers a dictionary of
diff --git a/pyramid/config/util.py b/pyramid/config/util.py
index a4df44408..1c6e1ca15 100644
--- a/pyramid/config/util.py
+++ b/pyramid/config/util.py
@@ -12,8 +12,8 @@ from pyramid.compat import (
)
from pyramid.exceptions import ConfigurationError
-
from pyramid.registry import predvalseq
+from pyramid.util import TopologicalSorter
from hashlib import md5
@@ -72,156 +72,6 @@ def as_sorted_tuple(val):
# under = after
# over = before
-class Singleton(object):
- def __init__(self, repr):
- self.repr = repr
-
- def __repr__(self):
- return self.repr
-
-FIRST = Singleton('FIRST')
-LAST = Singleton('LAST')
-
-class TopologicalSorter(object):
- def __init__(
- self,
- default_before=LAST,
- default_after=None,
- first=FIRST,
- last=LAST,
- ):
- self.names = []
- self.req_before = set()
- self.req_after = set()
- self.name2before = {}
- self.name2after = {}
- self.name2val = {}
- self.order = []
- self.default_before = default_before
- self.default_after = default_after
- self.first = first
- self.last = last
-
- def remove(self, name):
- self.names.remove(name)
- del self.name2val[name]
- after = self.name2after.pop(name, [])
- if after:
- self.req_after.remove(name)
- for u in after:
- self.order.remove((u, name))
- before = self.name2before.pop(name, [])
- if before:
- self.req_before.remove(name)
- for u in before:
- self.order.remove((name, u))
-
- def add(self, name, val, after=None, before=None):
- if name in self.names:
- self.remove(name)
- self.names.append(name)
- self.name2val[name] = val
- if after is None and before is None:
- before = self.default_before
- after = self.default_after
- if after is not None:
- if not is_nonstr_iter(after):
- after = (after,)
- self.name2after[name] = after
- self.order += [(u, name) for u in after]
- self.req_after.add(name)
- if before is not None:
- if not is_nonstr_iter(before):
- before = (before,)
- self.name2before[name] = before
- self.order += [(name, o) for o in before]
- self.req_before.add(name)
-
- def sorted(self):
- order = [(self.first, self.last)]
- roots = []
- graph = {}
- names = [self.first, self.last]
- names.extend(self.names)
-
- for a, b in self.order:
- order.append((a, b))
-
- def add_node(node):
- if not node in graph:
- 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)
-
- for name in names:
- add_node(name)
-
- has_before, has_after = set(), set()
- for a, b in order:
- if a in names and b in names: # deal with missing dependencies
- add_arc(a, b)
- has_before.add(a)
- has_after.add(b)
-
- if not self.req_before.issubset(has_before):
- raise ConfigurationError(
- 'Unsatisfied before dependencies: %s'
- % (', '.join(sorted(self.req_before - has_before)))
- )
- if not self.req_after.issubset(has_after):
- raise ConfigurationError(
- 'Unsatisfied after dependencies: %s'
- % (', '.join(sorted(self.req_after - has_after)))
- )
-
- sorted_names = []
-
- while roots:
- root = roots.pop(0)
- sorted_names.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 name in sorted_names:
- if name in self.names:
- result.append((name, self.name2val[name]))
-
- return result
-
-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 before %r' % (dependent, dependees))
- msg = 'Implicit ordering cycle:' + '; '.join(L)
- return msg
-
class PredicateList(object):
def __init__(self):
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index 36896a17e..b01d17efd 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -662,6 +662,7 @@ class ViewsConfiguratorMixin(object):
mapper=None,
http_cache=None,
match_param=None,
+ check_csrf=None,
**predicates):
""" Add a :term:`view configuration` to the current
configuration state. Arguments to ``add_view`` are broken
@@ -903,11 +904,12 @@ class ViewsConfiguratorMixin(object):
request_param
- This value can be any string. A view declaration with this
- argument ensures that the view will only be called when the
- :term:`request` has a key in the ``request.params``
+ This value can be any string or any sequence of strings. A view
+ declaration with this argument ensures that the view will only be
+ called when the :term:`request` has a key in the ``request.params``
dictionary (an HTTP ``GET`` or ``POST`` variable) that has a
- name which matches the supplied value. If the value
+ name which matches the supplied value (if the value is a string)
+ or values (if the value is a tuple). If any value
supplied has a ``=`` sign in it,
e.g. ``request_param="foo=123"``, then the key (``foo``)
must both exist in the ``request.params`` dictionary, *and*
@@ -989,6 +991,58 @@ class ViewsConfiguratorMixin(object):
variable. If the regex matches, this predicate will be
``True``.
+ check_csrf
+
+ If specified, this value should be one of ``None``, ``True``,
+ ``False``, or a string representing the 'check name'. If the value
+ is ``True`` or a string, CSRF checking will be performed. If the
+ value is ``False`` or ``None``, CSRF checking will not be performed.
+
+ If the value provided is a string, that string will be used as the
+ 'check name'. If the value provided is ``True``, ``csrf_token`` will
+ be used as the check name.
+
+ If CSRF checking is performed, the checked value will be the value
+ of ``request.params[check_name]``. This value will be compared
+ against the value of ``request.session.get_csrf_token()``, and the
+ check will pass if these two values are the same. If the check
+ passes, the associated view will be permitted to execute. If the
+ check fails, the associated view will not be permitted to execute.
+
+ Note that using this feature requires a :term:`session factory` to
+ have been configured.
+
+ .. versionadded:: 1.4a2
+
+ physical_path
+
+ If specified, this value should be a string or a tuple representing
+ the :term:`physical path` of the context found via traversal for this
+ predicate to match as true. For example: ``physical_path='/'`` or
+ ``physical_path='/a/b/c'`` or ``physical_path=('', 'a', 'b', 'c')``.
+ This is not a path prefix match or a regex, it's a whole-path match.
+ It's useful when you want to always potentially show a view when some
+ object is traversed to, but you can't be sure about what kind of
+ object it will be, so you can't use the ``context`` predicate. The
+ individual path elements inbetween slash characters or in tuple
+ elements should be the Unicode representation of the name of the
+ resource and should not be encoded in any way.
+
+ .. versionadded:: 1.4a3
+
+ effective_principals
+
+ If specified, this value should be a :term:`principal` identifier or
+ a sequence of principal identifiers. If the
+ :func:`pyramid.security.effective_principals` method indicates that
+ every principal named in the argument list is present in the current
+ request, this predicate will return True; otherwise it will return
+ False. For example:
+ ``effective_principals=pyramid.security.Authenticated`` or
+ ``effective_principals=('fred', 'group:admins')``.
+
+ .. versionadded:: 1.4a4
+
custom_predicates
This value should be a sequence of references to custom
@@ -1007,7 +1061,9 @@ class ViewsConfiguratorMixin(object):
:meth:`pyramid.config.Configurator.add_view_predicate`. More than
one key/value pair can be used at the same time. See
:ref:`view_and_route_predicates` for more information about
- third-party predicates. This argument is new as of Pyramid 1.4.
+ third-party predicates.
+
+ .. versionadded: 1.4a1
"""
view = self.maybe_dotted(view)
@@ -1061,6 +1117,7 @@ class ViewsConfiguratorMixin(object):
containment=containment,
request_type=request_type,
match_param=match_param,
+ check_csrf=check_csrf,
custom=predvalseq(custom_predicates),
)
)
@@ -1098,6 +1155,7 @@ class ViewsConfiguratorMixin(object):
header=header,
path_info=path_info,
match_param=match_param,
+ check_csrf=check_csrf,
callable=view,
mapper=mapper,
decorator=decorator,
@@ -1340,6 +1398,9 @@ class ViewsConfiguratorMixin(object):
('containment', p.ContainmentPredicate),
('request_type', p.RequestTypePredicate),
('match_param', p.MatchParamPredicate),
+ ('check_csrf', p.CheckCSRFTokenPredicate),
+ ('physical_path', p.PhysicalPathPredicate),
+ ('effective_principals', p.EffectivePrincipalsPredicate),
('custom', p.CustomPredicate),
):
self.add_view_predicate(name, factory)
diff --git a/pyramid/decorator.py b/pyramid/decorator.py
index 98d7b36b5..82d2b1280 100644
--- a/pyramid/decorator.py
+++ b/pyramid/decorator.py
@@ -1,9 +1,31 @@
class reify(object):
+ """ Use as a class method decorator. It operates almost exactly like the
+ Python ``@property`` decorator, but it puts the result of the method it
+ decorates into the instance dict after the first call, effectively
+ replacing the function it decorates with an instance variable. It is, in
+ Python parlance, a non-data descriptor. An example:
- """ Put the result of a method which uses this (non-data)
- descriptor decorator in the instance dict after the first call,
- effectively replacing the decorator with an instance variable."""
+ .. code-block:: python
+ class Foo(object):
+ @reify
+ def jammy(self):
+ print 'jammy called'
+ return 1
+
+ And usage of Foo:
+
+ .. code-block:: text
+
+ >>> f = Foo()
+ >>> v = f.jammy
+ 'jammy called'
+ >>> print v
+ 1
+ >>> f.jammy
+ 1
+ >>> # jammy func not called the second time; it replaced itself with 1
+ """
def __init__(self, wrapped):
self.wrapped = wrapped
try:
diff --git a/pyramid/exceptions.py b/pyramid/exceptions.py
index 04b6e20b7..1c8f99f62 100644
--- a/pyramid/exceptions.py
+++ b/pyramid/exceptions.py
@@ -60,3 +60,21 @@ class ConfigurationExecutionError(ConfigurationError):
def __str__(self):
return "%s: %s\n in:\n %s" % (self.etype, self.evalue, self.info)
+
+class CyclicDependencyError(Exception):
+ """ The exception raised when the Pyramid topological sorter detects a
+ cyclic dependency."""
+ 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 before %r' % (dependent, dependees))
+ msg = 'Implicit ordering cycle:' + '; '.join(L)
+ return msg
+
+
diff --git a/pyramid/mako_templating.py b/pyramid/mako_templating.py
index 2b09e8d45..f2627c1fc 100644
--- a/pyramid/mako_templating.py
+++ b/pyramid/mako_templating.py
@@ -91,11 +91,13 @@ class MakoRendererFactoryHelper(object):
def __call__(self, info):
p = re.compile(
- r'(?P<asset>[\w_.:/]+)'
+ r'(?P<asset>[\w_.:/-]+)'
r'(?:\#(?P<defname>[\w_]+))?'
r'(\.(?P<ext>.*))'
)
- asset, defname, ext = p.match(info.name).group('asset', 'defname', 'ext')
+ asset, defname, ext = p.match(info.name).group(
+ 'asset', 'defname', 'ext'
+ )
path = '%s.%s' % (asset, ext)
registry = info.registry
settings = info.settings
@@ -154,12 +156,9 @@ class MakoRendererFactoryHelper(object):
preprocessor=preprocessor
)
- registry_lock.acquire()
- try:
+ with registry_lock:
registry.registerUtility(lookup, IMakoLookup,
name=settings_prefix)
- finally:
- registry_lock.release()
return MakoLookupTemplateRenderer(path, defname, lookup)
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index fe9df33d1..6839d72f5 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -367,6 +367,12 @@ class JSONP(JSON):
@implementer(IChameleonLookup)
class ChameleonRendererLookup(object):
+ spec_re = re.compile(
+ r'(?P<asset>[\w_.:/-]+)'
+ r'(?:\#(?P<defname>[\w_]+))?'
+ r'(\.(?P<ext>.*))'
+ )
+
def __init__(self, impl, registry):
self.impl = impl
self.registry = registry
@@ -417,6 +423,12 @@ class ChameleonRendererLookup(object):
return False
return settings.get('reload_templates', False)
+ def _crack_spec(self, spec):
+ asset, macro, ext = self.spec_re.match(spec).group(
+ 'asset', 'defname', 'ext'
+ )
+ return asset, macro, ext
+
def __call__(self, info):
spec = self.get_spec(info.name, info.package)
registry = info.registry
@@ -429,46 +441,36 @@ class ChameleonRendererLookup(object):
if renderer is None:
renderer = self.impl(spec, self, macro=None)
# cache the template
- try:
- self.lock.acquire()
+ with self.lock:
registry.registerUtility(renderer,
ITemplateRenderer, name=spec)
- finally:
- self.lock.release()
else:
# spec is a package:relpath asset spec
renderer = registry.queryUtility(ITemplateRenderer, name=spec)
if renderer is None:
- p = re.compile(
- r'(?P<asset>[\w_.:/]+)'
- r'(?:\#(?P<defname>[\w_]+))?'
- r'(\.(?P<ext>.*))'
- )
- asset, macro, ext = p.match(spec).group(
- 'asset', 'defname', 'ext')
- spec = '%s.%s' % (asset, ext)
+ asset, macro, ext = self._crack_spec(spec)
+ spec_without_macro = '%s.%s' % (asset, ext)
try:
- package_name, filename = spec.split(':', 1)
+ package_name, filename = spec_without_macro.split(':', 1)
except ValueError: # pragma: no cover
# somehow we were passed a relative pathname; this
# should die
package_name = caller_package(4).__name__
- filename = spec
+ filename = spec_without_macro
abspath = pkg_resources.resource_filename(package_name,
filename)
if not pkg_resources.resource_exists(package_name, filename):
raise ValueError(
- 'Missing template asset: %s (%s)' % (spec, abspath))
+ 'Missing template asset: %s (%s)' % (
+ spec_without_macro, abspath)
+ )
renderer = self.impl(abspath, self, macro=macro)
settings = info.settings
if not settings.get('reload_assets'):
# cache the template
- self.lock.acquire()
- try:
+ with self.lock:
registry.registerUtility(renderer, ITemplateRenderer,
name=spec)
- finally:
- self.lock.release()
return renderer
@@ -479,11 +481,8 @@ def template_renderer_factory(info, impl, lock=registry_lock):
lookup = registry.queryUtility(IChameleonLookup, name=info.type)
if lookup is None:
lookup = ChameleonRendererLookup(impl, registry)
- lock.acquire()
- try:
+ with lock:
registry.registerUtility(lookup, IChameleonLookup, name=info.type)
- finally:
- lock.release()
return lookup(info)
@implementer(IRendererInfo)
@@ -574,13 +573,11 @@ class RendererHelper(object):
response = response_factory()
- if result is None:
- result = ''
-
- if isinstance(result, text_type):
- response.text = result
- else:
- response.body = result
+ if result is not None:
+ if isinstance(result, text_type):
+ response.text = result
+ else:
+ response.body = result
if request is not None:
# deprecated mechanism to set up request.response_* attrs, see
diff --git a/pyramid/request.py b/pyramid/request.py
index af3310829..9e275c2c0 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -328,6 +328,8 @@ class Request(BaseRequest, DeprecatedRequestMethodsMixin, URLMethodsMixin,
matchdict = None
matched_route = None
+ ResponseClass = Response
+
@reify
def tmpl_context(self):
# docs-deprecated template context for Pylons-like apps; do not
diff --git a/pyramid/router.py b/pyramid/router.py
index 0cbe00f3a..0c7f61071 100644
--- a/pyramid/router.py
+++ b/pyramid/router.py
@@ -53,6 +53,7 @@ class Router(object):
tweens = q(ITweens)
if tweens is None:
tweens = excview_tween_factory
+ self.orig_handle_request = self.handle_request
self.handle_request = tweens(self.handle_request, registry)
self.root_policy = self.root_factory # b/w compat
self.registry = registry
@@ -161,35 +162,75 @@ class Router(object):
return response
- def __call__(self, environ, start_response):
+ def invoke_subrequest(self, request, use_tweens=False):
"""
- Accept ``environ`` and ``start_response``; create a
- :term:`request` and route the request to a :app:`Pyramid`
- view based on introspection of :term:`view configuration`
- within the application registry; call ``start_response`` and
- return an iterable.
+ Obtain a response object from the Pyramid application based on
+ information in the ``request`` object provided. The ``request``
+ object must be an object that implements the Pyramid request
+ interface (such as a :class:`pyramid.request.Request` instance). If
+ ``use_tweens`` is ``True``, the request will be sent to the
+ :term:`tween` in the tween stack closest to the request ingress. If
+ ``use_tweens`` is ``False``, the request will be sent to the main
+ router handler, and no tweens will be invoked. This function also:
+
+ - manages the threadlocal stack (so that
+ :func:`~pyramid.threadlocal.get_current_request` and
+ :func:`~pyramid.threadlocal.get_current_registry` work during a
+ request)
+
+ - Adds a ``registry`` attribute and a ``invoke_subrequest`` attribute
+ (a callable) to the request object it's handed.
+
+ - sets request extensions (such as those added via
+ :meth:`~pyramid.config.Configurator.add_request_method` or
+ :meth:`~pyramid.config.Configurator.set_request_property`) on the
+ request it's passed.
+
+ - causes a :class:`~pyramid.event.NewRequest` event to be sent at the
+ beginning of request processing.
+
+ - causes a :class:`~pyramid.event.ContextFound` event to be sent
+ when a context resource is found.
+
+ - causes a :class:`~pyramid.event.NewResponse` event to be sent when
+ the Pyramid application returns a response.
+
+ - Calls any :term:`response callback` functions defined within the
+ request's lifetime if a response is obtained from the Pyramid
+ application.
+
+ - Calls any :term:`finished callback` functions defined within the
+ request's lifetime.
+
+ See also :ref:`subrequest_chapter`.
"""
registry = self.registry
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
+ request.invoke_subrequest = self.invoke_subrequest
+
+ if use_tweens:
+ handle_request = self.handle_request
+ else:
+ handle_request = self.orig_handle_request
+
try:
try:
extensions = self.request_extensions
if extensions is not None:
request._set_extensions(extensions)
- response = self.handle_request(request)
+ response = 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)
+ return response
finally:
if request.finished_callbacks:
@@ -197,3 +238,16 @@ class Router(object):
finally:
manager.pop()
+
+ def __call__(self, environ, start_response):
+ """
+ Accept ``environ`` and ``start_response``; create a
+ :term:`request` and route the request to a :app:`Pyramid`
+ view based on introspection of :term:`view configuration`
+ within the application registry; call ``start_response`` and
+ return an iterable.
+ """
+ request = self.request_factory(environ)
+ response = self.invoke_subrequest(request, use_tweens=True)
+ return response(request.environ, start_response)
+
diff --git a/pyramid/scaffolds/alchemy/+package+/__init__.py b/pyramid/scaffolds/alchemy/+package+/__init__.py
index 253341563..e39f619ed 100644
--- a/pyramid/scaffolds/alchemy/+package+/__init__.py
+++ b/pyramid/scaffolds/alchemy/+package+/__init__.py
@@ -1,13 +1,17 @@
from pyramid.config import Configurator
from sqlalchemy import engine_from_config
-from .models import DBSession
+from .models import (
+ DBSession,
+ Base,
+ )
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
config = Configurator(settings=settings)
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
diff --git a/pyramid/scaffolds/alchemy/+package+/static/pylons.css b/pyramid/scaffolds/alchemy/+package+/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/pyramid/scaffolds/alchemy/+package+/static/pylons.css
+++ b/pyramid/scaffolds/alchemy/+package+/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/pyramid/scaffolds/alchemy/+package+/templates/mytemplate.pt_tmpl b/pyramid/scaffolds/alchemy/+package+/templates/mytemplate.pt_tmpl
index ac0140789..99606fe0e 100644
--- a/pyramid/scaffolds/alchemy/+package+/templates/mytemplate.pt_tmpl
+++ b/pyramid/scaffolds/alchemy/+package+/templates/mytemplate.pt_tmpl
@@ -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/en/1.3-branch/search.html">
+ <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/search.html">
<input type="text" id="q" name="q" value="" />
<input type="submit" id="x" value="Go" />
</form>
@@ -44,22 +44,22 @@
<a href="http://pylonsproject.org">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#narrative-documentation">Narrative Documentation</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#reference-material">API Documentation</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#reference-material">API Documentation</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#tutorials">Tutorials</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#tutorials">Tutorials</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#detailed-change-history">Change History</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#detailed-change-history">Change History</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#sample-applications">Sample Applications</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#sample-applications">Sample Applications</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#support-and-development">Support and Development</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#support-and-development">Support and Development</a>
</li>
<li>
<a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
@@ -70,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">&copy; Copyright 2008-2011, Agendaless Consulting.</div>
+ <div class="footer">&copy; Copyright 2008-2012, Agendaless Consulting.</div>
</div>
</body>
</html>
diff --git a/pyramid/scaffolds/alchemy/development.ini_tmpl b/pyramid/scaffolds/alchemy/development.ini_tmpl
index eebfbcc3e..bdf08171c 100644
--- a/pyramid/scaffolds/alchemy/development.ini_tmpl
+++ b/pyramid/scaffolds/alchemy/development.ini_tmpl
@@ -1,3 +1,8 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
[app:main]
use = egg:{{project}}
@@ -12,12 +17,23 @@ pyramid.includes =
sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite
+# By default, the toolbar only appears for clients from IP addresses
+# '127.0.0.1' and '::1'.
+# debugtoolbar.hosts = 127.0.0.1 ::1
+
+###
+# wsgi server configuration
+###
+
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
-# Begin logging configuration
+###
+# logging configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
+###
[loggers]
keys = root, {{package_logger}}, sqlalchemy
@@ -53,5 +69,3 @@ formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
diff --git a/pyramid/scaffolds/alchemy/production.ini_tmpl b/pyramid/scaffolds/alchemy/production.ini_tmpl
index 9488f1811..69b08e458 100644
--- a/pyramid/scaffolds/alchemy/production.ini_tmpl
+++ b/pyramid/scaffolds/alchemy/production.ini_tmpl
@@ -1,3 +1,8 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
[app:main]
use = egg:{{project}}
@@ -16,7 +21,10 @@ use = egg:waitress#main
host = 0.0.0.0
port = 6543
-# Begin logging configuration
+###
+# logging configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
+###
[loggers]
keys = root, {{package_logger}}, sqlalchemy
@@ -52,5 +60,3 @@ formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
diff --git a/pyramid/scaffolds/starter/+package+/static/pylons.css b/pyramid/scaffolds/starter/+package+/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/pyramid/scaffolds/starter/+package+/static/pylons.css
+++ b/pyramid/scaffolds/starter/+package+/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl b/pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl
index 743eab026..4a71dd992 100644
--- a/pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl
+++ b/pyramid/scaffolds/starter/+package+/templates/mytemplate.pt_tmpl
@@ -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/en/1.3-branch/search.html">
+ <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/search.html">
<input type="text" id="q" name="q" value="" />
<input type="submit" id="x" value="Go" />
</form>
@@ -44,22 +44,22 @@
<a href="http://pylonsproject.org">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#narrative-documentation">Narrative Documentation</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#reference-material">API Documentation</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#reference-material">API Documentation</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#tutorials">Tutorials</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#tutorials">Tutorials</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#detailed-change-history">Change History</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#detailed-change-history">Change History</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#sample-applications">Sample Applications</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#sample-applications">Sample Applications</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#support-and-development">Support and Development</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#support-and-development">Support and Development</a>
</li>
<li>
<a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
@@ -70,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">&copy; Copyright 2008-2011, Agendaless Consulting.</div>
+ <div class="footer">&copy; Copyright 2008-2012, Agendaless Consulting.</div>
</div>
</body>
</html>
diff --git a/pyramid/scaffolds/starter/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl
index c92e13906..33c454086 100644
--- a/pyramid/scaffolds/starter/development.ini_tmpl
+++ b/pyramid/scaffolds/starter/development.ini_tmpl
@@ -1,3 +1,8 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
[app:main]
use = egg:{{project}}
@@ -9,12 +14,23 @@ pyramid.default_locale_name = en
pyramid.includes =
pyramid_debugtoolbar
+# By default, the toolbar only appears for clients from IP addresses
+# '127.0.0.1' and '::1'.
+# debugtoolbar.hosts = 127.0.0.1 ::1
+
+###
+# wsgi server configuration
+###
+
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
-# Begin logging configuration
+###
+# logging configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
+###
[loggers]
keys = root, {{package_logger}}
@@ -42,5 +58,3 @@ formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
diff --git a/pyramid/scaffolds/starter/production.ini_tmpl b/pyramid/scaffolds/starter/production.ini_tmpl
index 28957b5c1..dd2637e5b 100644
--- a/pyramid/scaffolds/starter/production.ini_tmpl
+++ b/pyramid/scaffolds/starter/production.ini_tmpl
@@ -1,3 +1,8 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
[app:main]
use = egg:{{project}}
@@ -7,12 +12,19 @@ pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
+###
+# wsgi server configuration
+###
+
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
-# Begin logging configuration
+###
+# logging configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
+###
[loggers]
keys = root, {{package_logger}}
@@ -40,5 +52,3 @@ formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
diff --git a/pyramid/scaffolds/zodb/+package+/static/pylons.css b/pyramid/scaffolds/zodb/+package+/static/pylons.css
index c54499ddd..4b1c017cd 100644
--- a/pyramid/scaffolds/zodb/+package+/static/pylons.css
+++ b/pyramid/scaffolds/zodb/+package+/static/pylons.css
@@ -1,65 +1,372 @@
-html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
-vertical-align:baseline;background:transparent;}
-body{line-height:1;}
-ol,ul{list-style:none;}
-blockquote,q{quotes:none;}
-blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-:focus{outline:0;}
-ins{text-decoration:none;}
-del{text-decoration:line-through;}
-table{border-collapse:collapse;border-spacing:0;}
-sub{vertical-align:sub;font-size:smaller;line-height:normal;}
-sup{vertical-align:super;font-size:smaller;line-height:normal;}
-ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
-ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
-li{display:list-item;}
-ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
-ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
-ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
-.hidden{display:none;}
-p{line-height:1.5em;}
-h1{font-size:1.75em;line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
-h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
-html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#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,
-body h2,
-body h3,
-body h4,
-body h5,
-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;}
-#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
-.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
-.wrapper{width:100%}
-#top,#top-small,#bottom{width:100%;}
-#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
-#bottom{color:#222;background-color:#ffffff;}
-.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
-.top{padding-top:40px;}
-.top-small{padding-top:10px;}
-#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
-.app-welcome{margin-top:25px;}
-.app-name{color:#000000;font-weight:bold;}
-.bottom{padding-top:50px;}
-#left{width:350px;float:left;padding-right:25px;}
-#right{width:350px;float:right;padding-left:25px;}
-.align-left{text-align:left;}
-.align-right{text-align:right;}
-.align-center{text-align:center;}
-ul.links{margin:0;padding:0;}
-ul.links li{list-style-type:none;font-size:14px;}
-form{border-style:none;}
-fieldset{border-style:none;}
-input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text],input[type=password]{width:205px;}
-input[type=submit]{background-color:#ddd;font-weight:bold;}
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
/*Opera Fix*/
-body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt b/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt
index d64f18fca..5391509fe 100644
--- a/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt
+++ b/pyramid/scaffolds/zodb/+package+/templates/mytemplate.pt
@@ -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/en/1.3-branch/search.html">
+ <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/search.html">
<input type="text" id="q" name="q" value="" />
<input type="submit" id="x" value="Go" />
</form>
@@ -44,22 +44,22 @@
<a href="http://pylonsproject.org">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#narrative-documentation">Narrative Documentation</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#reference-material">API Documentation</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#reference-material">API Documentation</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#tutorials">Tutorials</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#tutorials">Tutorials</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#detailed-change-history">Change History</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#detailed-change-history">Change History</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#sample-applications">Sample Applications</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#sample-applications">Sample Applications</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/#support-and-development">Support and Development</a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#support-and-development">Support and Development</a>
</li>
<li>
<a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
@@ -70,7 +70,7 @@
</div>
</div>
<div id="footer">
- <div class="footer">&copy; Copyright 2008-2011, Agendaless Consulting.</div>
+ <div class="footer">&copy; Copyright 2008-2012, Agendaless Consulting.</div>
</div>
</body>
</html>
diff --git a/pyramid/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl
index 1260b4db3..746f7ded3 100644
--- a/pyramid/scaffolds/zodb/development.ini_tmpl
+++ b/pyramid/scaffolds/zodb/development.ini_tmpl
@@ -1,3 +1,8 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
[app:main]
use = egg:{{project}}
@@ -14,12 +19,23 @@ pyramid.includes =
tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
+# By default, the toolbar only appears for clients from IP addresses
+# '127.0.0.1' and '::1'.
+# debugtoolbar.hosts = 127.0.0.1 ::1
+
+###
+# wsgi server configuration
+###
+
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
-# Begin logging configuration
+###
+# logging configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
+###
[loggers]
keys = root, {{package_logger}}
@@ -47,5 +63,3 @@ formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
diff --git a/pyramid/scaffolds/zodb/production.ini_tmpl b/pyramid/scaffolds/zodb/production.ini_tmpl
index 1640009fa..9ce639ec3 100644
--- a/pyramid/scaffolds/zodb/production.ini_tmpl
+++ b/pyramid/scaffolds/zodb/production.ini_tmpl
@@ -1,3 +1,8 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
[app:main]
use = egg:{{project}}
@@ -13,12 +18,19 @@ pyramid.includes =
tm.attempts = 3
zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
+###
+# wsgi server configuration
+###
+
[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543
-# Begin logging configuration
+###
+# logging configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
+###
[loggers]
keys = root, {{package_logger}}
@@ -46,5 +58,3 @@ formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
-
-# End logging configuration
diff --git a/pyramid/scripting.py b/pyramid/scripting.py
index 00177986f..fdb4aa430 100644
--- a/pyramid/scripting.py
+++ b/pyramid/scripting.py
@@ -71,6 +71,10 @@ def prepare(request=None, registry=None):
'before trying to activate it.')
if request is None:
request = _make_request('/', registry)
+ # NB: even though _make_request might have already set registry on
+ # request, we reset it in case someone has passed in their own
+ # request.
+ request.registry = registry
threadlocals = {'registry':registry, 'request':request}
threadlocal_manager.push(threadlocals)
extensions = registry.queryUtility(IRequestExtensions)
diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py
index ea2a4ae09..9fbf0729a 100644
--- a/pyramid/scripts/pserve.py
+++ b/pyramid/scripts/pserve.py
@@ -583,12 +583,8 @@ class LazyWriter(object):
def open(self):
if self.fileobj is None:
- self.lock.acquire()
- try:
- if self.fileobj is None:
- self.fileobj = open(self.filename, self.mode)
- finally:
- self.lock.release()
+ with self.lock:
+ self.fileobj = open(self.filename, self.mode)
return self.fileobj
def close(self):
diff --git a/pyramid/security.py b/pyramid/security.py
index 4b929241e..3e25f9b2f 100644
--- a/pyramid/security.py
+++ b/pyramid/security.py
@@ -4,6 +4,7 @@ from pyramid.interfaces import (
IAuthenticationPolicy,
IAuthorizationPolicy,
ISecuredView,
+ IView,
IViewClassifier,
)
@@ -132,7 +133,13 @@ def view_execution_permitted(context, request, name=''):
view using the effective authentication/authorization policies and
the ``request``. Return a boolean result. If no
:term:`authorization policy` is in effect, or if the view is not
- protected by a permission, return ``True``."""
+ protected by a permission, return ``True``. If no view can view found,
+ an exception will be raised.
+
+ .. versionchanged:: 1.4a4
+ An exception is raised if no view is found.
+
+ """
try:
reg = request.registry
except AttributeError:
@@ -140,6 +147,11 @@ def view_execution_permitted(context, request, name=''):
provides = [IViewClassifier] + map_(providedBy, (request, context))
view = reg.adapters.lookup(provides, ISecuredView, name=name)
if view is None:
+ view = reg.adapters.lookup(provides, IView, name=name)
+ if view is None:
+ raise TypeError('No registered view satisfies the constraints. '
+ 'It would not make sense to claim that this view '
+ '"is" or "is not" permitted.')
return Allowed(
'Allowed: view name %r in context %r (no permission defined)' %
(name, context))
diff --git a/pyramid/session.py b/pyramid/session.py
index 40e21ddbc..a5e6a8d3a 100644
--- a/pyramid/session.py
+++ b/pyramid/session.py
@@ -15,6 +15,7 @@ from pyramid.compat import (
native_,
)
+from pyramid.httpexceptions import HTTPBadRequest
from pyramid.interfaces import ISession
from pyramid.util import strings_differ
@@ -80,6 +81,29 @@ def signed_deserialize(serialized, secret, hmac=hmac):
return pickle.loads(pickled)
+def check_csrf_token(request, token='csrf_token', raises=True):
+ """ Check the CSRF token in the request's session against the value in
+ ``request.params.get(token)``. If a ``token`` keyword is not supplied
+ to this function, the string ``csrf_token`` will be used to look up
+ the token within ``request.params``. If the value in
+ ``request.params.get(token)`` doesn't match the value supplied by
+ ``request.session.get_csrf_token()``, and ``raises`` is ``True``, this
+ function will raise an :exc:`pyramid.httpexceptions.HTTPBadRequest`
+ exception. If the check does succeed and ``raises`` is ``False``, this
+ function will return ``False``. If the CSRF check is successful, this
+ function will return ``True`` unconditionally.
+
+ Note that using this function requires that a :term:`session factory` is
+ configured.
+
+ .. versionadded:: 1.4a2
+ """
+ if request.params.get(token) != request.session.get_csrf_token():
+ if raises:
+ raise HTTPBadRequest('incorrect CSRF token')
+ return False
+ return True
+
def UnencryptedCookieSessionFactoryConfig(
secret,
timeout=1200,
diff --git a/pyramid/testing.py b/pyramid/testing.py
index 750effb83..cecf13469 100644
--- a/pyramid/testing.py
+++ b/pyramid/testing.py
@@ -1,5 +1,6 @@
import copy
import os
+from contextlib import contextmanager
from zope.interface import (
implementer,
@@ -52,10 +53,17 @@ class DummyRootFactory(object):
class DummySecurityPolicy(object):
""" A standin for both an IAuthentication and IAuthorization policy """
- def __init__(self, userid=None, groupids=(), permissive=True):
+ def __init__(self, userid=None, groupids=(), permissive=True,
+ remember_result=None, forget_result=None):
self.userid = userid
self.groupids = groupids
self.permissive = permissive
+ if remember_result is None:
+ remember_result = []
+ if forget_result is None:
+ forget_result = []
+ self.remember_result = remember_result
+ self.forget_result = forget_result
def authenticated_userid(self, request):
return self.userid
@@ -72,10 +80,12 @@ class DummySecurityPolicy(object):
return effective_principals
def remember(self, request, principal, **kw):
- return []
+ self.remembered = principal
+ return self.remember_result
def forget(self, request):
- return []
+ self.forgotten = True
+ return self.forget_result
def permits(self, context, principals, permission):
return self.permissive
@@ -293,7 +303,7 @@ class DummyRequest(DeprecatedRequestMethodsMixin, URLMethodsMixin,
request. For example, by default, the DummyRequest ``GET`` and ``POST``
attributes are of type ``dict``, unlike a normal Request's GET and POST,
which are of type ``MultiDict``. If your code uses the features of
- MultiDict, you should either use a"real" :class:`pyramid.request.Request`
+ MultiDict, you should either use a real :class:`pyramid.request.Request`
or adapt your DummyRequest by replacing the attributes with ``MultiDict``
instances.
@@ -572,3 +582,37 @@ def skip_on(*platforms): # pragma: no cover
return wrapper
return decorator
skip_on.os_name = os.name # for testing
+
+@contextmanager
+def testConfig(registry=None,
+ request=None,
+ hook_zca=True,
+ autocommit=True,
+ settings=None):
+ """Returns a context manager for test set up.
+
+ This context manager calls :func:`pyramid.testing.testSetup` when
+ entering and :func:`pyramid.testing.tearDown` when exiting.
+
+ All arguments are passed directly to :func:`pyramid.testing.testSetup`.
+ If the ZCA is hooked, it will always be un-hooked in tearDown.
+
+ This context manager allows you to write test code like this:
+
+ .. code-block:: python
+ :linenos:
+
+ with testConfig() as config:
+ config.add_route('bar', '/bar/{id}')
+ req = DummyRequest()
+ resp = myview(req),
+ """
+ config = setUp(registry=registry,
+ request=request,
+ hook_zca=hook_zca,
+ autocommit=autocommit,
+ settings=settings)
+ try:
+ yield config
+ finally:
+ tearDown(unhook_zca=hook_zca)
diff --git a/pyramid/tests/fixtures/withmacro.pt b/pyramid/tests/fixtures/withmacro.pt
index 8bca01e4d..6fa654645 100644
--- a/pyramid/tests/fixtures/withmacro.pt
+++ b/pyramid/tests/fixtures/withmacro.pt
@@ -1,4 +1,5 @@
<html>
+Outside macro
<metal:m define-macro="foo">
Hello!
</metal:m>
diff --git a/pyramid/tests/pkgs/subrequestapp/__init__.py b/pyramid/tests/pkgs/subrequestapp/__init__.py
new file mode 100644
index 000000000..b8f44cd7f
--- /dev/null
+++ b/pyramid/tests/pkgs/subrequestapp/__init__.py
@@ -0,0 +1,50 @@
+from pyramid.config import Configurator
+from pyramid.request import Request
+
+def view_one(request):
+ subreq = Request.blank('/view_two')
+ response = request.invoke_subrequest(subreq, use_tweens=False)
+ return response
+
+def view_two(request):
+ return 'This came from view_two'
+
+def view_three(request):
+ subreq = Request.blank('/view_four')
+ try:
+ return request.invoke_subrequest(subreq, use_tweens=True)
+ except: # pragma: no cover
+ request.response.body = b'Value error raised'
+ return request.response
+
+def view_four(request):
+ raise ValueError('foo')
+
+def view_five(request):
+ subreq = Request.blank('/view_four')
+ try:
+ return request.invoke_subrequest(subreq, use_tweens=False)
+ except ValueError:
+ request.response.body = b'Value error raised'
+ return request.response
+
+def excview(request):
+ request.response.status_int = 500
+ request.response.body = b'Bad stuff happened'
+ return request.response
+
+def main():
+ config = Configurator()
+ config.add_route('one', '/view_one')
+ config.add_route('two', '/view_two')
+ config.add_route('three', '/view_three')
+ config.add_route('four', '/view_four')
+ config.add_route('five', '/view_five')
+ config.add_view(excview, context=Exception)
+ config.add_view(view_one, route_name='one')
+ config.add_view(view_two, route_name='two', renderer='string')
+ config.add_view(view_three, route_name='three')
+ config.add_view(view_four, route_name='four')
+ config.add_view(view_five, route_name='five')
+ return config
+
diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py
index ae517fc40..b1a88b915 100644
--- a/pyramid/tests/test_authentication.py
+++ b/pyramid/tests/test_authentication.py
@@ -14,7 +14,7 @@ class TestCallbackAuthenticationPolicyDebugging(unittest.TestCase):
def tearDown(self):
del self.config
-
+
def debug(self, msg):
self.messages.append(msg)
@@ -76,6 +76,30 @@ class TestCallbackAuthenticationPolicyDebugging(unittest.TestCase):
"authenticated_userid: groupfinder callback returned []; "
"returning 'fred'")
+ def test_authenticated_userid_fails_cleaning_as_Authenticated(self):
+ request = DummyRequest(registry=self.config.registry)
+ policy = self._makeOne(userid='system.Authenticated')
+ self.assertEqual(policy.authenticated_userid(request), None)
+ self.assertEqual(len(self.messages), 1)
+ self.assertEqual(
+ self.messages[0],
+ "pyramid.tests.test_authentication.MyAuthenticationPolicy."
+ "authenticated_userid: use of userid 'system.Authenticated' is "
+ "disallowed by any built-in Pyramid security policy, returning "
+ "None")
+
+ def test_authenticated_userid_fails_cleaning_as_Everyone(self):
+ request = DummyRequest(registry=self.config.registry)
+ policy = self._makeOne(userid='system.Everyone')
+ self.assertEqual(policy.authenticated_userid(request), None)
+ self.assertEqual(len(self.messages), 1)
+ self.assertEqual(
+ self.messages[0],
+ "pyramid.tests.test_authentication.MyAuthenticationPolicy."
+ "authenticated_userid: use of userid 'system.Everyone' is "
+ "disallowed by any built-in Pyramid security policy, returning "
+ "None")
+
def test_effective_principals_no_unauthenticated_userid(self):
request = DummyRequest(registry=self.config.registry)
policy = self._makeOne()
@@ -144,6 +168,34 @@ class TestCallbackAuthenticationPolicyDebugging(unittest.TestCase):
"effective_principals: returning effective principals: "
"['system.Everyone', 'system.Authenticated', 'fred']")
+ def test_effective_principals_with_unclean_principal_Authenticated(self):
+ request = DummyRequest(registry=self.config.registry)
+ policy = self._makeOne(userid='system.Authenticated')
+ self.assertEqual(
+ policy.effective_principals(request),
+ ['system.Everyone'])
+ self.assertEqual(len(self.messages), 1)
+ self.assertEqual(
+ self.messages[0],
+ "pyramid.tests.test_authentication.MyAuthenticationPolicy."
+ "effective_principals: unauthenticated_userid returned disallowed "
+ "'system.Authenticated'; returning ['system.Everyone'] as if it "
+ "was None")
+
+ def test_effective_principals_with_unclean_principal_Everyone(self):
+ request = DummyRequest(registry=self.config.registry)
+ policy = self._makeOne(userid='system.Everyone')
+ self.assertEqual(
+ policy.effective_principals(request),
+ ['system.Everyone'])
+ self.assertEqual(len(self.messages), 1)
+ self.assertEqual(
+ self.messages[0],
+ "pyramid.tests.test_authentication.MyAuthenticationPolicy."
+ "effective_principals: unauthenticated_userid returned disallowed "
+ "'system.Everyone'; returning ['system.Everyone'] as if it "
+ "was None")
+
class TestRepozeWho1AuthenticationPolicy(unittest.TestCase):
def _getTargetClass(self):
from pyramid.authentication import RepozeWho1AuthenticationPolicy
@@ -151,7 +203,7 @@ class TestRepozeWho1AuthenticationPolicy(unittest.TestCase):
def _makeOne(self, identifier_name='auth_tkt', callback=None):
return self._getTargetClass()(identifier_name, callback)
-
+
def test_class_implements_IAuthenticationPolicy(self):
from zope.interface.verify import verifyClass
from pyramid.interfaces import IAuthenticationPolicy
@@ -184,6 +236,12 @@ class TestRepozeWho1AuthenticationPolicy(unittest.TestCase):
policy = self._makeOne()
self.assertEqual(policy.authenticated_userid(request), 'fred')
+ def test_authenticated_userid_repoze_who_userid_is_None(self):
+ request = DummyRequest(
+ {'repoze.who.identity':{'repoze.who.userid':None}})
+ policy = self._makeOne()
+ self.assertEqual(policy.authenticated_userid(request), None)
+
def test_authenticated_userid_with_callback_returns_None(self):
request = DummyRequest(
{'repoze.who.identity':{'repoze.who.userid':'fred'}})
@@ -200,6 +258,20 @@ class TestRepozeWho1AuthenticationPolicy(unittest.TestCase):
policy = self._makeOne(callback=callback)
self.assertEqual(policy.authenticated_userid(request), 'fred')
+ def test_authenticated_userid_unclean_principal_Authenticated(self):
+ request = DummyRequest(
+ {'repoze.who.identity':{'repoze.who.userid':'system.Authenticated'}}
+ )
+ policy = self._makeOne()
+ self.assertEqual(policy.authenticated_userid(request), None)
+
+ def test_authenticated_userid_unclean_principal_Everyone(self):
+ request = DummyRequest(
+ {'repoze.who.identity':{'repoze.who.userid':'system.Everyone'}}
+ )
+ policy = self._makeOne()
+ self.assertEqual(policy.authenticated_userid(request), None)
+
def test_effective_principals_None(self):
from pyramid.security import Everyone
request = DummyRequest({})
@@ -237,6 +309,31 @@ class TestRepozeWho1AuthenticationPolicy(unittest.TestCase):
policy = self._makeOne(callback=callback)
self.assertEqual(policy.effective_principals(request), [Everyone])
+ def test_effective_principals_repoze_who_userid_is_None(self):
+ from pyramid.security import Everyone
+ request = DummyRequest(
+ {'repoze.who.identity':{'repoze.who.userid':None}}
+ )
+ policy = self._makeOne()
+ self.assertEqual(policy.effective_principals(request), [Everyone])
+
+ def test_effective_principals_repoze_who_userid_is_unclean_Everyone(self):
+ from pyramid.security import Everyone
+ request = DummyRequest(
+ {'repoze.who.identity':{'repoze.who.userid':'system.Everyone'}}
+ )
+ policy = self._makeOne()
+ self.assertEqual(policy.effective_principals(request), [Everyone])
+
+ def test_effective_principals_repoze_who_userid_is_unclean_Authenticated(
+ self):
+ from pyramid.security import Everyone
+ request = DummyRequest(
+ {'repoze.who.identity':{'repoze.who.userid':'system.Authenticated'}}
+ )
+ policy = self._makeOne()
+ self.assertEqual(policy.effective_principals(request), [Everyone])
+
def test_remember_no_plugins(self):
request = DummyRequest({})
policy = self._makeOne()
@@ -251,7 +348,7 @@ class TestRepozeWho1AuthenticationPolicy(unittest.TestCase):
result = policy.remember(request, 'fred')
self.assertEqual(result[0], request.environ)
self.assertEqual(result[1], {'repoze.who.userid':'fred'})
-
+
def test_forget_no_plugins(self):
request = DummyRequest({})
policy = self._makeOne()
@@ -276,7 +373,7 @@ class TestRemoteUserAuthenticationPolicy(unittest.TestCase):
def _makeOne(self, environ_key='REMOTE_USER', callback=None):
return self._getTargetClass()(environ_key, callback)
-
+
def test_class_implements_IAuthenticationPolicy(self):
from zope.interface.verify import verifyClass
from pyramid.interfaces import IAuthenticationPolicy
@@ -301,7 +398,7 @@ class TestRemoteUserAuthenticationPolicy(unittest.TestCase):
request = DummyRequest({})
policy = self._makeOne()
self.assertEqual(policy.authenticated_userid(request), None)
-
+
def test_authenticated_userid(self):
request = DummyRequest({'REMOTE_USER':'fred'})
policy = self._makeOne()
@@ -326,7 +423,7 @@ class TestRemoteUserAuthenticationPolicy(unittest.TestCase):
policy = self._makeOne()
result = policy.remember(request, 'fred')
self.assertEqual(result, [])
-
+
def test_forget(self):
request = DummyRequest({'REMOTE_USER':'fred'})
policy = self._makeOne()
@@ -423,7 +520,7 @@ class TestBaseAutkTktAuthenticationPolicy(unittest.TestCase):
request = DummyRequest({})
policy = self._makeOne(None, None)
self.assertEqual(policy.authenticated_userid(request), None)
-
+
def test_authenticated_userid_callback_returns_None(self):
request = DummyRequest({})
def callback(userid, request):
@@ -474,7 +571,7 @@ class TestBaseAutkTktAuthenticationPolicy(unittest.TestCase):
result = policy.remember(request, 'fred', a=1, b=2)
self.assertEqual(policy.cookie.kw, {'a':1, 'b':2})
self.assertEqual(result, [])
-
+
def test_forget(self):
request = DummyRequest({})
policy = self._makeOne(None, None)
@@ -530,7 +627,7 @@ class TestAuthTktCookieHelper(unittest.TestCase):
request = self._makeRequest(None)
result = helper.identify(request)
self.assertEqual(result, None)
-
+
def test_identify_good_cookie_include_ip(self):
helper = self._makeOne('secret', include_ip=True)
request = self._makeRequest('ticket')
@@ -653,7 +750,7 @@ class TestAuthTktCookieHelper(unittest.TestCase):
request = self._makeRequest('ticket')
result = helper.identify(request)
self.assertEqual(result, None)
-
+
def test_identify_cookie_timed_out(self):
helper = self._makeOne('secret', timeout=1)
request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'})
@@ -876,7 +973,7 @@ class TestAuthTktCookieHelper(unittest.TestCase):
self.assertEqual(result[1][0], 'Set-Cookie')
self.assertTrue(result[1][1].endswith('; Path=/; Domain=example.com'))
self.assertTrue(result[1][1].startswith('auth_tkt='))
-
+
def test_remember_binary_userid(self):
import base64
helper = self._makeOne('secret')
@@ -1169,6 +1266,78 @@ class TestSessionAuthenticationPolicy(unittest.TestCase):
self.assertEqual(request.session.get('userid'), None)
self.assertEqual(result, [])
+class TestBasicAuthAuthenticationPolicy(unittest.TestCase):
+ def _getTargetClass(self):
+ from pyramid.authentication import BasicAuthAuthenticationPolicy as cls
+ return cls
+
+ def _makeOne(self, check):
+ return self._getTargetClass()(check, realm='SomeRealm')
+
+ def test_class_implements_IAuthenticationPolicy(self):
+ from zope.interface.verify import verifyClass
+ from pyramid.interfaces import IAuthenticationPolicy
+ verifyClass(IAuthenticationPolicy, self._getTargetClass())
+
+ def test_unauthenticated_userid(self):
+ import base64
+ request = testing.DummyRequest()
+ request.headers['Authorization'] = 'Basic %s' % base64.b64encode(
+ bytes_('chrisr:password')).decode('ascii')
+ policy = self._makeOne(None)
+ self.assertEqual(policy.unauthenticated_userid(request), 'chrisr')
+
+ def test_unauthenticated_userid_no_credentials(self):
+ request = testing.DummyRequest()
+ policy = self._makeOne(None)
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_unauthenticated_bad_header(self):
+ request = testing.DummyRequest()
+ request.headers['Authorization'] = '...'
+ policy = self._makeOne(None)
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_unauthenticated_userid_not_basic(self):
+ request = testing.DummyRequest()
+ request.headers['Authorization'] = 'Complicated things'
+ policy = self._makeOne(None)
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_unauthenticated_userid_corrupt_base64(self):
+ request = testing.DummyRequest()
+ request.headers['Authorization'] = 'Basic chrisr:password'
+ policy = self._makeOne(None)
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_authenticated_userid(self):
+ import base64
+ request = testing.DummyRequest()
+ request.headers['Authorization'] = 'Basic %s' % base64.b64encode(
+ bytes_('chrisr:password')).decode('ascii')
+ def check(username, password, request):
+ return []
+ policy = self._makeOne(check)
+ self.assertEqual(policy.authenticated_userid(request), 'chrisr')
+
+ def test_unauthenticated_userid_invalid_payload(self):
+ import base64
+ request = testing.DummyRequest()
+ request.headers['Authorization'] = 'Basic %s' % base64.b64encode(
+ bytes_('chrisrpassword')).decode('ascii')
+ policy = self._makeOne(None)
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_remember(self):
+ policy = self._makeOne(None)
+ self.assertEqual(policy.remember(None, None), [])
+
+ def test_forget(self):
+ policy = self._makeOne(None)
+ self.assertEqual(policy.forget(None), [
+ ('WWW-Authenticate', 'Basic realm="SomeRealm"')])
+
+
class DummyContext:
pass
@@ -1193,7 +1362,7 @@ class DummyRequest:
class DummyWhoPlugin:
def remember(self, environ, identity):
return environ, identity
-
+
def forget(self, environ, identity):
return environ, identity
@@ -1228,7 +1397,7 @@ class DummyAuthTktModule(object):
raise self.BadTicket()
return self.timestamp, self.userid, self.tokens, self.user_data
self.parse_ticket = parse_ticket
-
+
class AuthTicket(object):
def __init__(self, secret, userid, remote_addr, **kw):
self.secret = secret
@@ -1250,4 +1419,4 @@ class DummyAuthTktModule(object):
class DummyResponse:
def __init__(self):
self.headerlist = []
-
+
diff --git a/pyramid/tests/test_chameleon_text.py b/pyramid/tests/test_chameleon_text.py
index 297e96554..d9f20f241 100644
--- a/pyramid/tests/test_chameleon_text.py
+++ b/pyramid/tests/test_chameleon_text.py
@@ -2,7 +2,6 @@ import sys
import unittest
from pyramid.compat import binary_type
-from pyramid.testing import skip_on
from pyramid import testing
class Base(object):
@@ -50,7 +49,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
from pyramid.interfaces import ITemplateRenderer
verifyClass(ITemplateRenderer, self._getTargetClass())
- @skip_on('java')
def test_template_reified(self):
minimal = self._getTemplatePath('minimal.txt')
lookup = DummyLookup()
@@ -59,7 +57,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template, instance.__dict__['template'])
- @skip_on('java')
def test_template_with_ichameleon_translate(self):
minimal = self._getTemplatePath('minimal.txt')
lookup = DummyLookup()
@@ -68,7 +65,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template.translate, lookup.translate)
- @skip_on('java')
def test_template_with_debug_templates(self):
minimal = self._getTemplatePath('minimal.txt')
lookup = DummyLookup()
@@ -78,7 +74,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template.debug, True)
- @skip_on('java')
def test_template_with_reload_templates(self):
minimal = self._getTemplatePath('minimal.txt')
lookup = DummyLookup()
@@ -88,7 +83,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template.auto_reload, True)
- @skip_on('java')
def test_template_without_reload_templates(self):
minimal = self._getTemplatePath('minimal.txt')
lookup = DummyLookup()
@@ -98,7 +92,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template.auto_reload, False)
- @skip_on('java')
def test_call(self):
minimal = self._getTemplatePath('minimal.txt')
lookup = DummyLookup()
@@ -107,14 +100,12 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
self.assertTrue(isinstance(result, binary_type))
self.assertEqual(result, b'Hello.\n')
- @skip_on('java')
def test_call_with_nondict_value(self):
minimal = self._getTemplatePath('minimal.txt')
lookup = DummyLookup()
instance = self._makeOne(minimal, lookup)
self.assertRaises(ValueError, instance, None, {})
- @skip_on('java')
def test_call_nonminimal(self):
nonminimal = self._getTemplatePath('nonminimal.txt')
lookup = DummyLookup()
@@ -123,7 +114,6 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
self.assertTrue(isinstance(result, binary_type))
self.assertEqual(result, b'Hello, Chris!\n')
- @skip_on('java')
def test_implementation(self):
minimal = self._getTemplatePath('minimal.txt')
lookup = DummyLookup()
diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py
index 5d197dac4..5ac57f869 100644
--- a/pyramid/tests/test_chameleon_zpt.py
+++ b/pyramid/tests/test_chameleon_zpt.py
@@ -1,7 +1,6 @@
import sys
import unittest
-from pyramid.testing import skip_on
from pyramid import testing
from pyramid.compat import text_type
@@ -50,7 +49,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
from pyramid.interfaces import ITemplateRenderer
verifyClass(ITemplateRenderer, self._getTargetClass())
- @skip_on('java')
def test_call(self):
minimal = self._getTemplatePath('minimal.pt')
lookup = DummyLookup()
@@ -60,7 +58,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
self.assertEqual(result.rstrip('\n'),
'<div xmlns="http://www.w3.org/1999/xhtml">\n</div>')
- @skip_on('java')
def test_template_reified(self):
minimal = self._getTemplatePath('minimal.pt')
lookup = DummyLookup()
@@ -69,7 +66,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template, instance.__dict__['template'])
- @skip_on('java')
def test_template_with_ichameleon_translate(self):
minimal = self._getTemplatePath('minimal.pt')
lookup = DummyLookup()
@@ -78,7 +74,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template.translate, lookup.translate)
- @skip_on('java')
def test_template_with_debug_templates(self):
minimal = self._getTemplatePath('minimal.pt')
lookup = DummyLookup()
@@ -88,7 +83,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template.debug, True)
- @skip_on('java')
def test_template_without_debug_templates(self):
minimal = self._getTemplatePath('minimal.pt')
lookup = DummyLookup()
@@ -98,7 +92,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template.debug, False)
- @skip_on('java')
def test_template_with_reload_templates(self):
minimal = self._getTemplatePath('minimal.pt')
lookup = DummyLookup()
@@ -108,7 +101,6 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template.auto_reload, True)
- @skip_on('java')
def test_template_without_reload_templates(self):
minimal = self._getTemplatePath('minimal.pt')
lookup = DummyLookup()
@@ -118,14 +110,12 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
template = instance.template
self.assertEqual(template.auto_reload, False)
- @skip_on('java')
def test_call_with_nondict_value(self):
minimal = self._getTemplatePath('minimal.pt')
lookup = DummyLookup()
instance = self._makeOne(minimal, lookup)
self.assertRaises(ValueError, instance, None, {})
- @skip_on('java')
def test_implementation(self):
minimal = self._getTemplatePath('minimal.pt')
lookup = DummyLookup()
@@ -142,8 +132,13 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
result = instance.implementation()()
self.assertEqual(result, '\n Hello!\n')
-
-
+ def test_macro_notsupplied(self):
+ minimal = self._getTemplatePath('withmacro.pt')
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
+ result = instance.implementation()()
+ self.assertEqual(result,
+ '<html>\nOutside macro\n\n Hello!\n\n</html>\n\n')
class DummyLookup(object):
auto_reload=True
diff --git a/pyramid/tests/test_config/test_init.py b/pyramid/tests/test_config/test_init.py
index f39906dd9..2cf9a269a 100644
--- a/pyramid/tests/test_config/test_init.py
+++ b/pyramid/tests/test_config/test_init.py
@@ -923,12 +923,13 @@ pyramid.tests.test_config.dummy_include2""",
result = render_view_to_response(ctx, req, 'another_stacked_class2')
self.assertEqual(result, 'another_stacked_class')
- if not os.name.startswith('java'):
- # on Jython, a class without an __init__ apparently accepts
- # any number of arguments without raising a TypeError.
+ # NB: on Jython, a class without an __init__ apparently accepts
+ # any number of arguments without raising a TypeError, so the next
+ # assertion may fail there. We don't support Jython at the moment,
+ # this is just a note to a future self.
- self.assertRaises(TypeError,
- render_view_to_response, ctx, req, 'basemethod')
+ self.assertRaises(TypeError,
+ render_view_to_response, ctx, req, 'basemethod')
result = render_view_to_response(ctx, req, 'method1')
self.assertEqual(result, 'method1')
diff --git a/pyramid/tests/test_config/test_init.py:TestConfigurator_add_directive b/pyramid/tests/test_config/test_init.py:TestConfigurator_add_directive
deleted file mode 100644
index e69de29bb..000000000
--- a/pyramid/tests/test_config/test_init.py:TestConfigurator_add_directive
+++ /dev/null
diff --git a/pyramid/tests/test_config/test_predicates.py b/pyramid/tests/test_config/test_predicates.py
index e33a31458..91dfb0fb6 100644
--- a/pyramid/tests/test_config/test_predicates.py
+++ b/pyramid/tests/test_config/test_predicates.py
@@ -1,5 +1,7 @@
import unittest
+from pyramid import testing
+
from pyramid.compat import text_
class TestXHRPredicate(unittest.TestCase):
@@ -117,6 +119,20 @@ class TestRequestParamPredicate(unittest.TestCase):
result = inst(None, request)
self.assertTrue(result)
+ def test___call___true_multi(self):
+ inst = self._makeOne(('abc', 'def =2 '))
+ request = Dummy()
+ request.params = {'abc':'1', 'def': '2'}
+ result = inst(None, request)
+ self.assertTrue(result)
+
+ def test___call___false_multi(self):
+ inst = self._makeOne(('abc=3', 'def =2 '))
+ request = Dummy()
+ request.params = {'abc':'3', 'def': '1'}
+ result = inst(None, request)
+ self.assertFalse(result)
+
def test___call___false(self):
inst = self._makeOne('abc')
request = Dummy()
@@ -130,7 +146,11 @@ class TestRequestParamPredicate(unittest.TestCase):
def test_text_withval(self):
inst = self._makeOne('abc= 1')
- self.assertEqual(inst.text(), 'request_param abc = 1')
+ self.assertEqual(inst.text(), 'request_param abc=1')
+
+ def test_text_multi(self):
+ inst = self._makeOne(('abc= 1', 'def'))
+ self.assertEqual(inst.text(), 'request_param abc=1,def')
def test_phash_exists(self):
inst = self._makeOne('abc')
@@ -138,7 +158,7 @@ class TestRequestParamPredicate(unittest.TestCase):
def test_phash_withval(self):
inst = self._makeOne('abc= 1')
- self.assertEqual(inst.phash(), "request_param abc = 1")
+ self.assertEqual(inst.phash(), "request_param abc=1")
class TestMatchParamPredicate(unittest.TestCase):
def _makeOne(self, val):
@@ -256,6 +276,220 @@ class TestTraversePredicate(unittest.TestCase):
inst = self._makeOne('/abc')
self.assertEqual(inst.phash(), '')
+class Test_CheckCSRFTokenPredicate(unittest.TestCase):
+ def _makeOne(self, val, config):
+ from pyramid.config.predicates import CheckCSRFTokenPredicate
+ return CheckCSRFTokenPredicate(val, config)
+
+ def test_text(self):
+ inst = self._makeOne(True, None)
+ self.assertEqual(inst.text(), 'check_csrf = True')
+
+ def test_phash(self):
+ inst = self._makeOne(True, None)
+ self.assertEqual(inst.phash(), 'check_csrf = True')
+
+ def test_it_call_val_True(self):
+ inst = self._makeOne(True, None)
+ request = Dummy()
+ def check_csrf_token(req, val, raises=True):
+ self.assertEqual(req, request)
+ self.assertEqual(val, 'csrf_token')
+ self.assertEqual(raises, False)
+ return True
+ inst.check_csrf_token = check_csrf_token
+ result = inst(None, request)
+ self.assertEqual(result, True)
+
+ def test_it_call_val_str(self):
+ inst = self._makeOne('abc', None)
+ request = Dummy()
+ def check_csrf_token(req, val, raises=True):
+ self.assertEqual(req, request)
+ self.assertEqual(val, 'abc')
+ self.assertEqual(raises, False)
+ return True
+ inst.check_csrf_token = check_csrf_token
+ result = inst(None, request)
+ self.assertEqual(result, True)
+
+ def test_it_call_val_False(self):
+ inst = self._makeOne(False, None)
+ request = Dummy()
+ result = inst(None, request)
+ self.assertEqual(result, True)
+
+class TestHeaderPredicate(unittest.TestCase):
+ def _makeOne(self, val):
+ from pyramid.config.predicates import HeaderPredicate
+ return HeaderPredicate(val, None)
+
+ def test___call___true_exists(self):
+ inst = self._makeOne('abc')
+ request = Dummy()
+ request.headers = {'abc':1}
+ result = inst(None, request)
+ self.assertTrue(result)
+
+ def test___call___true_withval(self):
+ inst = self._makeOne('abc:1')
+ request = Dummy()
+ request.headers = {'abc':'1'}
+ result = inst(None, request)
+ self.assertTrue(result)
+
+ def test___call___true_withregex(self):
+ inst = self._makeOne(r'abc:\d+')
+ request = Dummy()
+ request.headers = {'abc':'1'}
+ result = inst(None, request)
+ self.assertTrue(result)
+
+ def test___call___false_withregex(self):
+ inst = self._makeOne(r'abc:\d+')
+ request = Dummy()
+ request.headers = {'abc':'a'}
+ result = inst(None, request)
+ self.assertFalse(result)
+
+ def test___call___false(self):
+ inst = self._makeOne('abc')
+ request = Dummy()
+ request.headers = {}
+ result = inst(None, request)
+ self.assertFalse(result)
+
+ def test_text_exists(self):
+ inst = self._makeOne('abc')
+ self.assertEqual(inst.text(), 'header abc')
+
+ def test_text_withval(self):
+ inst = self._makeOne('abc:1')
+ self.assertEqual(inst.text(), 'header abc=1')
+
+ def test_text_withregex(self):
+ inst = self._makeOne(r'abc:\d+')
+ self.assertEqual(inst.text(), r'header abc=\d+')
+
+ def test_phash_exists(self):
+ inst = self._makeOne('abc')
+ self.assertEqual(inst.phash(), 'header abc')
+
+ def test_phash_withval(self):
+ inst = self._makeOne('abc:1')
+ self.assertEqual(inst.phash(), "header abc=1")
+
+ def test_phash_withregex(self):
+ inst = self._makeOne(r'abc:\d+')
+ self.assertEqual(inst.phash(), r'header abc=\d+')
+
+class Test_PhysicalPathPredicate(unittest.TestCase):
+ def _makeOne(self, val, config):
+ from pyramid.config.predicates import PhysicalPathPredicate
+ return PhysicalPathPredicate(val, config)
+
+ def test_text(self):
+ inst = self._makeOne('/', None)
+ self.assertEqual(inst.text(), "physical_path = ('',)")
+
+ def test_phash(self):
+ inst = self._makeOne('/', None)
+ self.assertEqual(inst.phash(), "physical_path = ('',)")
+
+ def test_it_call_val_tuple_True(self):
+ inst = self._makeOne(('', 'abc'), None)
+ root = Dummy()
+ root.__name__ = ''
+ root.__parent__ = None
+ context = Dummy()
+ context.__name__ = 'abc'
+ context.__parent__ = root
+ self.assertTrue(inst(context, None))
+
+ def test_it_call_val_list_True(self):
+ inst = self._makeOne(['', 'abc'], None)
+ root = Dummy()
+ root.__name__ = ''
+ root.__parent__ = None
+ context = Dummy()
+ context.__name__ = 'abc'
+ context.__parent__ = root
+ self.assertTrue(inst(context, None))
+
+ def test_it_call_val_str_True(self):
+ inst = self._makeOne('/abc', None)
+ root = Dummy()
+ root.__name__ = ''
+ root.__parent__ = None
+ context = Dummy()
+ context.__name__ = 'abc'
+ context.__parent__ = root
+ self.assertTrue(inst(context, None))
+
+ def test_it_call_False(self):
+ inst = self._makeOne('/', None)
+ root = Dummy()
+ root.__name__ = ''
+ root.__parent__ = None
+ context = Dummy()
+ context.__name__ = 'abc'
+ context.__parent__ = root
+ self.assertFalse(inst(context, None))
+
+class Test_EffectivePrincipalsPredicate(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _makeOne(self, val, config):
+ from pyramid.config.predicates import EffectivePrincipalsPredicate
+ return EffectivePrincipalsPredicate(val, config)
+
+ def test_text(self):
+ inst = self._makeOne(('verna', 'fred'), None)
+ self.assertEqual(inst.text(),
+ "effective_principals = ['fred', 'verna']")
+
+ def test_text_noniter(self):
+ inst = self._makeOne('verna', None)
+ self.assertEqual(inst.text(),
+ "effective_principals = ['verna']")
+
+ def test_phash(self):
+ inst = self._makeOne(('verna', 'fred'), None)
+ self.assertEqual(inst.phash(),
+ "effective_principals = ['fred', 'verna']")
+
+ def test_it_call_no_authentication_policy(self):
+ request = testing.DummyRequest()
+ inst = self._makeOne(('verna', 'fred'), None)
+ context = Dummy()
+ self.assertFalse(inst(context, request))
+
+ def test_it_call_authentication_policy_provides_superset(self):
+ request = testing.DummyRequest()
+ self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi'))
+ inst = self._makeOne(('verna', 'fred'), None)
+ context = Dummy()
+ self.assertTrue(inst(context, request))
+
+ def test_it_call_authentication_policy_provides_superset_implicit(self):
+ from pyramid.security import Authenticated
+ request = testing.DummyRequest()
+ self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi'))
+ inst = self._makeOne(Authenticated, None)
+ context = Dummy()
+ self.assertTrue(inst(context, request))
+
+ def test_it_call_authentication_policy_doesnt_provide_superset(self):
+ request = testing.DummyRequest()
+ self.config.testing_securitypolicy('fred')
+ inst = self._makeOne(('verna', 'fred'), None)
+ context = Dummy()
+ self.assertFalse(inst(context, request))
+
class predicate(object):
def __repr__(self):
return 'predicate'
diff --git a/pyramid/tests/test_config/test_testing.py b/pyramid/tests/test_config/test_testing.py
index 6c048b46d..1089f09fc 100644
--- a/pyramid/tests/test_config/test_testing.py
+++ b/pyramid/tests/test_config/test_testing.py
@@ -23,6 +23,30 @@ class TestingConfiguratorMixinTests(unittest.TestCase):
self.assertEqual(ut.groupids, ('group1', 'group2'))
self.assertEqual(ut.permissive, False)
+ def test_testing_securitypolicy_remember_result(self):
+ from pyramid.security import remember
+ config = self._makeOne(autocommit=True)
+ pol = config.testing_securitypolicy(
+ 'user', ('group1', 'group2'),
+ permissive=False, remember_result=True)
+ request = DummyRequest()
+ request.registry = config.registry
+ val = remember(request, 'fred')
+ self.assertEqual(pol.remembered, 'fred')
+ self.assertEqual(val, True)
+
+ def test_testing_securitypolicy_forget_result(self):
+ from pyramid.security import forget
+ config = self._makeOne(autocommit=True)
+ pol = config.testing_securitypolicy(
+ 'user', ('group1', 'group2'),
+ permissive=False, forget_result=True)
+ request = DummyRequest()
+ request.registry = config.registry
+ val = forget(request)
+ self.assertEqual(pol.forgotten, True)
+ self.assertEqual(val, True)
+
def test_testing_resources(self):
from pyramid.traversal import find_resource
from pyramid.interfaces import ITraverser
diff --git a/pyramid/tests/test_config/test_tweens.py b/pyramid/tests/test_config/test_tweens.py
index 8853b6899..9c3433468 100644
--- a/pyramid/tests/test_config/test_tweens.py
+++ b/pyramid/tests/test_config/test_tweens.py
@@ -392,7 +392,7 @@ class TestTweens(unittest.TestCase):
self.assertRaises(ConfigurationError, tweens.implicit)
def test_implicit_ordering_conflict_direct(self):
- from pyramid.config.util import CyclicDependencyError
+ from pyramid.exceptions import CyclicDependencyError
tweens = self._makeOne()
add = tweens.add_implicit
add('browserid', 'browserid_factory')
@@ -400,7 +400,7 @@ class TestTweens(unittest.TestCase):
self.assertRaises(CyclicDependencyError, tweens.implicit)
def test_implicit_ordering_conflict_indirect(self):
- from pyramid.config.util import CyclicDependencyError
+ from pyramid.exceptions import CyclicDependencyError
tweens = self._makeOne()
add = tweens.add_implicit
add('browserid', 'browserid_factory')
diff --git a/pyramid/tests/test_config/test_util.py b/pyramid/tests/test_config/test_util.py
index 13cb27526..8c3cd7455 100644
--- a/pyramid/tests/test_config/test_util.py
+++ b/pyramid/tests/test_config/test_util.py
@@ -396,274 +396,6 @@ class TestActionInfo(unittest.TestCase):
self.assertEqual(str(inst),
"Line 0 of file filename:\n linerepr ")
-class TestTopologicalSorter(unittest.TestCase):
- def _makeOne(self, *arg, **kw):
- from pyramid.config.util import TopologicalSorter
- return TopologicalSorter(*arg, **kw)
-
- def test_remove(self):
- inst = self._makeOne()
- inst.names.append('name')
- inst.name2val['name'] = 1
- inst.req_after.add('name')
- inst.req_before.add('name')
- inst.name2after['name'] = ('bob',)
- inst.name2before['name'] = ('fred',)
- inst.order.append(('bob', 'name'))
- inst.order.append(('name', 'fred'))
- inst.remove('name')
- self.assertFalse(inst.names)
- self.assertFalse(inst.req_before)
- self.assertFalse(inst.req_after)
- self.assertFalse(inst.name2before)
- self.assertFalse(inst.name2after)
- self.assertFalse(inst.name2val)
- self.assertFalse(inst.order)
-
- def test_add(self):
- from pyramid.config.util import LAST
- sorter = self._makeOne()
- sorter.add('name', 'factory')
- self.assertEqual(sorter.names, ['name'])
- self.assertEqual(sorter.name2val,
- {'name':'factory'})
- self.assertEqual(sorter.order, [('name', LAST)])
- sorter.add('name2', 'factory2')
- self.assertEqual(sorter.names, ['name', 'name2'])
- self.assertEqual(sorter.name2val,
- {'name':'factory', 'name2':'factory2'})
- self.assertEqual(sorter.order,
- [('name', LAST), ('name2', LAST)])
- sorter.add('name3', 'factory3', before='name2')
- self.assertEqual(sorter.names,
- ['name', 'name2', 'name3'])
- self.assertEqual(sorter.name2val,
- {'name':'factory', 'name2':'factory2',
- 'name3':'factory3'})
- self.assertEqual(sorter.order,
- [('name', LAST), ('name2', LAST),
- ('name3', 'name2')])
-
- def test_sorted_ordering_1(self):
- sorter = self._makeOne()
- sorter.add('name1', 'factory1')
- sorter.add('name2', 'factory2')
- self.assertEqual(sorter.sorted(),
- [
- ('name1', 'factory1'),
- ('name2', 'factory2'),
- ])
-
- def test_sorted_ordering_2(self):
- from pyramid.config.util import FIRST
- sorter = self._makeOne()
- sorter.add('name1', 'factory1')
- sorter.add('name2', 'factory2', after=FIRST)
- self.assertEqual(sorter.sorted(),
- [
- ('name2', 'factory2'),
- ('name1', 'factory1'),
- ])
-
- def test_sorted_ordering_3(self):
- from pyramid.config.util import FIRST
- sorter = self._makeOne()
- add = sorter.add
- add('auth', 'auth_factory', after='browserid')
- add('dbt', 'dbt_factory')
- add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
- add('browserid', 'browserid_factory')
- add('txnmgr', 'txnmgr_factory', after='exceptionview')
- add('exceptionview', 'excview_factory', after=FIRST)
- self.assertEqual(sorter.sorted(),
- [
- ('exceptionview', 'excview_factory'),
- ('retry', 'retry_factory'),
- ('txnmgr', 'txnmgr_factory'),
- ('dbt', 'dbt_factory'),
- ('browserid', 'browserid_factory'),
- ('auth', 'auth_factory'),
- ])
-
- def test_sorted_ordering_4(self):
- from pyramid.config.util import FIRST
- sorter = self._makeOne()
- add = sorter.add
- add('exceptionview', 'excview_factory', after=FIRST)
- add('auth', 'auth_factory', after='browserid')
- add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
- add('browserid', 'browserid_factory')
- add('txnmgr', 'txnmgr_factory', after='exceptionview')
- add('dbt', 'dbt_factory')
- self.assertEqual(sorter.sorted(),
- [
- ('exceptionview', 'excview_factory'),
- ('retry', 'retry_factory'),
- ('txnmgr', 'txnmgr_factory'),
- ('browserid', 'browserid_factory'),
- ('auth', 'auth_factory'),
- ('dbt', 'dbt_factory'),
- ])
-
- def test_sorted_ordering_5(self):
- from pyramid.config.util import LAST, FIRST
- sorter = self._makeOne()
- add = sorter.add
- add('exceptionview', 'excview_factory')
- add('auth', 'auth_factory', after=FIRST)
- add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
- add('browserid', 'browserid_factory', after=FIRST)
- add('txnmgr', 'txnmgr_factory', after='exceptionview', before=LAST)
- add('dbt', 'dbt_factory')
- self.assertEqual(sorter.sorted(),
- [
- ('browserid', 'browserid_factory'),
- ('auth', 'auth_factory'),
- ('exceptionview', 'excview_factory'),
- ('retry', 'retry_factory'),
- ('txnmgr', 'txnmgr_factory'),
- ('dbt', 'dbt_factory'),
- ])
-
- def test_sorted_ordering_missing_before_partial(self):
- from pyramid.exceptions import ConfigurationError
- sorter = self._makeOne()
- add = sorter.add
- add('dbt', 'dbt_factory')
- add('auth', 'auth_factory', after='browserid')
- add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
- add('browserid', 'browserid_factory')
- self.assertRaises(ConfigurationError, sorter.sorted)
-
- def test_sorted_ordering_missing_after_partial(self):
- from pyramid.exceptions import ConfigurationError
- sorter = self._makeOne()
- add = sorter.add
- add('dbt', 'dbt_factory')
- add('auth', 'auth_factory', after='txnmgr')
- add('retry', 'retry_factory', before='dbt', after='exceptionview')
- add('browserid', 'browserid_factory')
- self.assertRaises(ConfigurationError, sorter.sorted)
-
- def test_sorted_ordering_missing_before_and_after_partials(self):
- from pyramid.exceptions import ConfigurationError
- sorter = self._makeOne()
- add = sorter.add
- add('dbt', 'dbt_factory')
- add('auth', 'auth_factory', after='browserid')
- add('retry', 'retry_factory', before='foo', after='txnmgr')
- add('browserid', 'browserid_factory')
- self.assertRaises(ConfigurationError, sorter.sorted)
-
- def test_sorted_ordering_missing_before_partial_with_fallback(self):
- from pyramid.config.util import LAST
- sorter = self._makeOne()
- add = sorter.add
- add('exceptionview', 'excview_factory', before=LAST)
- add('auth', 'auth_factory', after='browserid')
- add('retry', 'retry_factory', before=('txnmgr', LAST),
- after='exceptionview')
- add('browserid', 'browserid_factory')
- add('dbt', 'dbt_factory')
- self.assertEqual(sorter.sorted(),
- [
- ('exceptionview', 'excview_factory'),
- ('retry', 'retry_factory'),
- ('browserid', 'browserid_factory'),
- ('auth', 'auth_factory'),
- ('dbt', 'dbt_factory'),
- ])
-
- def test_sorted_ordering_missing_after_partial_with_fallback(self):
- from pyramid.config.util import FIRST
- sorter = self._makeOne()
- add = sorter.add
- add('exceptionview', 'excview_factory', after=FIRST)
- add('auth', 'auth_factory', after=('txnmgr','browserid'))
- add('retry', 'retry_factory', after='exceptionview')
- add('browserid', 'browserid_factory')
- add('dbt', 'dbt_factory')
- self.assertEqual(sorter.sorted(),
- [
- ('exceptionview', 'excview_factory'),
- ('retry', 'retry_factory'),
- ('browserid', 'browserid_factory'),
- ('auth', 'auth_factory'),
- ('dbt', 'dbt_factory'),
- ])
-
- def test_sorted_ordering_with_partial_fallbacks(self):
- from pyramid.config.util import LAST
- sorter = self._makeOne()
- add = sorter.add
- add('exceptionview', 'excview_factory', before=('wontbethere', LAST))
- add('retry', 'retry_factory', after='exceptionview')
- add('browserid', 'browserid_factory', before=('wont2', 'exceptionview'))
- self.assertEqual(sorter.sorted(),
- [
- ('browserid', 'browserid_factory'),
- ('exceptionview', 'excview_factory'),
- ('retry', 'retry_factory'),
- ])
-
- def test_sorted_ordering_with_multiple_matching_fallbacks(self):
- from pyramid.config.util import LAST
- sorter = self._makeOne()
- add = sorter.add
- add('exceptionview', 'excview_factory', before=LAST)
- add('retry', 'retry_factory', after='exceptionview')
- add('browserid', 'browserid_factory', before=('retry', 'exceptionview'))
- self.assertEqual(sorter.sorted(),
- [
- ('browserid', 'browserid_factory'),
- ('exceptionview', 'excview_factory'),
- ('retry', 'retry_factory'),
- ])
-
- def test_sorted_ordering_with_missing_fallbacks(self):
- from pyramid.exceptions import ConfigurationError
- from pyramid.config.util import LAST
- sorter = self._makeOne()
- add = sorter.add
- add('exceptionview', 'excview_factory', before=LAST)
- add('retry', 'retry_factory', after='exceptionview')
- add('browserid', 'browserid_factory', before=('txnmgr', 'auth'))
- self.assertRaises(ConfigurationError, sorter.sorted)
-
- def test_sorted_ordering_conflict_direct(self):
- from pyramid.config.util import CyclicDependencyError
- sorter = self._makeOne()
- add = sorter.add
- add('browserid', 'browserid_factory')
- add('auth', 'auth_factory', before='browserid', after='browserid')
- self.assertRaises(CyclicDependencyError, sorter.sorted)
-
- def test_sorted_ordering_conflict_indirect(self):
- from pyramid.config.util import CyclicDependencyError
- sorter = self._makeOne()
- add = sorter.add
- add('browserid', 'browserid_factory')
- add('auth', 'auth_factory', before='browserid')
- add('dbt', 'dbt_factory', after='browserid', before='auth')
- self.assertRaises(CyclicDependencyError, sorter.sorted)
-
-class TestSingleton(unittest.TestCase):
- def test_repr(self):
- from pyramid.config.util import Singleton
- r = repr(Singleton('ABC'))
- self.assertEqual(r, 'ABC')
-
-class TestCyclicDependencyError(unittest.TestCase):
- def _makeOne(self, cycles):
- from pyramid.config.util import CyclicDependencyError
- return CyclicDependencyError(cycles)
-
- def test___str__(self):
- exc = self._makeOne({'a':['c', 'd'], 'c':['a']})
- result = str(exc)
- self.assertTrue("'a' sorts before ['c', 'd']" in result)
- self.assertTrue("'c' sorts before ['a']" in result)
-
class DummyCustomPredicate(object):
def __init__(self):
self.__text__ = 'custom predicate'
diff --git a/pyramid/tests/test_exceptions.py b/pyramid/tests/test_exceptions.py
index 773767d89..aa5ebb376 100644
--- a/pyramid/tests/test_exceptions.py
+++ b/pyramid/tests/test_exceptions.py
@@ -74,3 +74,15 @@ class TestConfigurationExecutionError(unittest.TestCase):
exc = self._makeOne('etype', 'evalue', 'info')
self.assertEqual(str(exc), 'etype: evalue\n in:\n info')
+class TestCyclicDependencyError(unittest.TestCase):
+ def _makeOne(self, cycles):
+ from pyramid.exceptions import CyclicDependencyError
+ return CyclicDependencyError(cycles)
+
+ def test___str__(self):
+ exc = self._makeOne({'a':['c', 'd'], 'c':['a']})
+ result = str(exc)
+ self.assertTrue("'a' sorts before ['c', 'd']" in result)
+ self.assertTrue("'c' sorts before ['a']" in result)
+
+
diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py
index 590ba0760..9a8f842aa 100644
--- a/pyramid/tests/test_integration.py
+++ b/pyramid/tests/test_integration.py
@@ -578,26 +578,49 @@ class WSGIApp2AppTest(unittest.TestCase):
res = self.testapp.get('/hello', status=200)
self.assertTrue(b'Hello' in res.body)
-if os.name != 'java': # uses chameleon
- class RendererScanAppTest(IntegrationBase, unittest.TestCase):
- package = 'pyramid.tests.pkgs.rendererscanapp'
- def test_root(self):
- res = self.testapp.get('/one', status=200)
- self.assertTrue(b'One!' in res.body)
-
- def test_two(self):
- res = self.testapp.get('/two', status=200)
- self.assertTrue(b'Two!' in res.body)
-
- def test_rescan(self):
- self.config.scan('pyramid.tests.pkgs.rendererscanapp')
- app = self.config.make_wsgi_app()
- from webtest import TestApp
- testapp = TestApp(app)
- res = testapp.get('/one', status=200)
- self.assertTrue(b'One!' in res.body)
- res = testapp.get('/two', status=200)
- self.assertTrue(b'Two!' in res.body)
+class SubrequestAppTest(unittest.TestCase):
+ def setUp(self):
+ from pyramid.tests.pkgs.subrequestapp import main
+ config = main()
+ app = config.make_wsgi_app()
+ from webtest import TestApp
+ self.testapp = TestApp(app)
+ self.config = config
+
+ def tearDown(self):
+ self.config.end()
+
+ def test_one(self):
+ res = self.testapp.get('/view_one', status=200)
+ self.assertTrue(b'This came from view_two' in res.body)
+
+ def test_three(self):
+ res = self.testapp.get('/view_three', status=500)
+ self.assertTrue(b'Bad stuff happened' in res.body)
+
+ def test_five(self):
+ res = self.testapp.get('/view_five', status=200)
+ self.assertTrue(b'Value error raised' in res.body)
+
+class RendererScanAppTest(IntegrationBase, unittest.TestCase):
+ package = 'pyramid.tests.pkgs.rendererscanapp'
+ def test_root(self):
+ res = self.testapp.get('/one', status=200)
+ self.assertTrue(b'One!' in res.body)
+
+ def test_two(self):
+ res = self.testapp.get('/two', status=200)
+ self.assertTrue(b'Two!' in res.body)
+
+ def test_rescan(self):
+ self.config.scan('pyramid.tests.pkgs.rendererscanapp')
+ app = self.config.make_wsgi_app()
+ from webtest import TestApp
+ testapp = TestApp(app)
+ res = testapp.get('/one', status=200)
+ self.assertTrue(b'One!' in res.body)
+ res = testapp.get('/two', status=200)
+ self.assertTrue(b'Two!' in res.body)
class DummyContext(object):
pass
diff --git a/pyramid/tests/test_mako_templating.py b/pyramid/tests/test_mako_templating.py
index 97b2c679b..37264aa48 100644
--- a/pyramid/tests/test_mako_templating.py
+++ b/pyramid/tests/test_mako_templating.py
@@ -31,6 +31,20 @@ class Test_renderer_factory(Base, unittest.TestCase):
from pyramid.mako_templating import IMakoLookup
return self.config.registry.getUtility(IMakoLookup, name=name)
+ def test_hyphen_filenames(self):
+ from pyramid.mako_templating import renderer_factory
+
+ info = DummyRendererInfo({
+ 'name':'app:moon-and-world.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':{},
+ 'type': ''
+ })
+
+ result = renderer_factory(info)
+ self.assertEqual(result.path, 'app:moon-and-world.mak')
+
def test_no_directories(self):
info = DummyRendererInfo({
'name':'pyramid.tests:fixtures/helloworld.mak',
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index af9188abc..befb714bd 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -295,6 +295,7 @@ class TestChameleonRendererLookup(unittest.TestCase):
self.assertEqual(factory.kw, {'macro':None})
def test___call__spec_withmacro(self):
+ from pyramid.interfaces import ITemplateRenderer
import os
from pyramid import tests
module_name = tests.__name__
@@ -302,10 +303,11 @@ class TestChameleonRendererLookup(unittest.TestCase):
renderer = {}
factory = DummyFactory(renderer)
spec = '%s:%s' % (module_name, relpath)
+ reg = self.config.registry
info = DummyRendererInfo({
'name':spec,
'package':None,
- 'registry':self.config.registry,
+ 'registry':reg,
'settings':{},
'type':'type',
})
@@ -318,6 +320,9 @@ class TestChameleonRendererLookup(unittest.TestCase):
'withmacro.pt')
self.assertTrue(factory.path.startswith(path))
self.assertEqual(factory.kw, {'macro':'foo'})
+ self.assertTrue(
+ reg.getUtility(ITemplateRenderer, name=spec) is renderer
+ )
def test___call__reload_assets_true(self):
import pyramid.tests
@@ -658,13 +663,23 @@ class TestRendererHelper(unittest.TestCase):
response = helper._make_response(la.encode('utf-8'), request)
self.assertEqual(response.body, la.encode('utf-8'))
- def test__make_response_result_is_None(self):
+ def test__make_response_result_is_None_no_body(self):
from pyramid.response import Response
request = testing.DummyRequest()
request.response = Response()
helper = self._makeOne('loo.foo')
response = helper._make_response(None, request)
self.assertEqual(response.body, b'')
+
+ def test__make_response_result_is_None_existing_body_not_molested(self):
+ from pyramid.response import Response
+ request = testing.DummyRequest()
+ response = Response()
+ response.body = b'abc'
+ request.response = response
+ helper = self._makeOne('loo.foo')
+ response = helper._make_response(None, request)
+ self.assertEqual(response.body, b'abc')
def test__make_response_with_content_type(self):
from pyramid.response import Response
diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py
index 86cfd8b09..945e36a7f 100644
--- a/pyramid/tests/test_request.py
+++ b/pyramid/tests/test_request.py
@@ -42,13 +42,17 @@ class TestRequest(unittest.TestCase):
from zope.interface.verify import verifyClass
from pyramid.interfaces import IRequest
verifyClass(IRequest, self._getTargetClass())
- klass = self._getTargetClass()
def test_instance_conforms_to_IRequest(self):
from zope.interface.verify import verifyObject
from pyramid.interfaces import IRequest
verifyObject(IRequest, self._makeOne())
+ def test_ResponseClass_is_pyramid_Response(self):
+ from pyramid.response import Response
+ cls = self._getTargetClass()
+ self.assertEqual(cls.ResponseClass, Response)
+
def test_charset_defaults_to_utf8(self):
r = self._makeOne({'PATH_INFO':'/'})
self.assertEqual(r.charset, 'UTF-8')
diff --git a/pyramid/tests/test_scripting.py b/pyramid/tests/test_scripting.py
index 1ccc7af3b..a36d1ed71 100644
--- a/pyramid/tests/test_scripting.py
+++ b/pyramid/tests/test_scripting.py
@@ -72,7 +72,7 @@ class Test_prepare(unittest.TestCase):
self.assertEqual(self.default, self.manager.get())
self.assertEqual(request.context, root)
- def test_it_withrequest(self):
+ def test_it_withrequest_hasregistry(self):
request = DummyRequest({})
registry = request.registry = self._makeRegistry()
info = self._callFUT(request=request)
@@ -85,6 +85,17 @@ class Test_prepare(unittest.TestCase):
closer()
self.assertEqual(self.default, self.manager.get())
self.assertEqual(request.context, root)
+ self.assertEqual(request.registry, registry)
+
+ def test_it_withrequest_noregistry(self):
+ request = DummyRequest({})
+ registry = self._makeRegistry()
+ info = self._callFUT(request=request, registry=registry)
+ root, closer, request = info['root'], info['closer'], info['request']
+ closer()
+ self.assertEqual(request.context, root)
+ # should be set by prepare
+ self.assertEqual(request.registry, registry)
def test_it_with_request_and_registry(self):
request = DummyRequest({})
diff --git a/pyramid/tests/test_security.py b/pyramid/tests/test_security.py
index ba9538b01..e530e33ca 100644
--- a/pyramid/tests/test_security.py
+++ b/pyramid/tests/test_security.py
@@ -131,19 +131,37 @@ class TestViewExecutionPermitted(unittest.TestCase):
return checker
def test_no_permission(self):
+ from zope.interface import Interface
from pyramid.threadlocal import get_current_registry
from pyramid.interfaces import ISettings
+ from pyramid.interfaces import IView
+ from pyramid.interfaces import IViewClassifier
settings = dict(debug_authorization=True)
reg = get_current_registry()
reg.registerUtility(settings, ISettings)
context = DummyContext()
request = DummyRequest({})
+ class DummyView(object):
+ pass
+ view = DummyView()
+ reg.registerAdapter(view, (IViewClassifier, Interface, Interface),
+ IView, '')
result = self._callFUT(context, request, '')
msg = result.msg
self.assertTrue("Allowed: view name '' in context" in msg)
self.assertTrue('(no permission defined)' in msg)
self.assertEqual(result, True)
+ def test_no_view_registered(self):
+ from pyramid.threadlocal import get_current_registry
+ from pyramid.interfaces import ISettings
+ settings = dict(debug_authorization=True)
+ reg = get_current_registry()
+ reg.registerUtility(settings, ISettings)
+ context = DummyContext()
+ request = DummyRequest({})
+ self.assertRaises(TypeError, self._callFUT, context, request, '')
+
def test_with_permission(self):
from zope.interface import Interface
from zope.interface import directlyProvides
diff --git a/pyramid/tests/test_session.py b/pyramid/tests/test_session.py
index 5143b7a95..b3e0e20c4 100644
--- a/pyramid/tests/test_session.py
+++ b/pyramid/tests/test_session.py
@@ -355,6 +355,31 @@ class Test_signed_deserialize(unittest.TestCase):
serialized = 'bad' + serialize('123', 'secret')
self.assertRaises(ValueError, self._callFUT, serialized, 'secret')
+class Test_check_csrf_token(unittest.TestCase):
+ def _callFUT(self, request, token, raises=True):
+ from ..session import check_csrf_token
+ return check_csrf_token(request, token, raises=raises)
+
+ def test_success(self):
+ request = testing.DummyRequest()
+ request.params['csrf_token'] = request.session.get_csrf_token()
+ self.assertEqual(self._callFUT(request, 'csrf_token'), True)
+
+ def test_success_default_token(self):
+ from ..session import check_csrf_token
+ request = testing.DummyRequest()
+ request.params['csrf_token'] = request.session.get_csrf_token()
+ self.assertEqual(check_csrf_token(request), True)
+
+ def test_failure_raises(self):
+ from pyramid.httpexceptions import HTTPBadRequest
+ request = testing.DummyRequest()
+ self.assertRaises(HTTPBadRequest, self._callFUT, request, 'csrf_token')
+
+ def test_failure_no_raises(self):
+ request = testing.DummyRequest()
+ result = self._callFUT(request, 'csrf_token', raises=False)
+ self.assertEqual(result, False)
class DummySessionFactory(dict):
_dirty = False
diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py
index 339a39cd8..7f14462a2 100644
--- a/pyramid/tests/test_testing.py
+++ b/pyramid/tests/test_testing.py
@@ -646,3 +646,58 @@ class DummyRendererInfo(object):
def __init__(self, kw):
self.__dict__.update(kw)
+class Test_testConfig(unittest.TestCase):
+
+ def _setUp(self, **kw):
+ self._log.append(('setUp', kw))
+ return 'fake config'
+
+ def _tearDown(self, **kw):
+ self._log.append(('tearDown', kw))
+
+ def setUp(self):
+ from pyramid import testing
+ self._log = []
+ self._orig_setUp = testing.setUp
+ testing.setUp = self._setUp
+ self._orig_tearDown = testing.tearDown
+ testing.tearDown = self._tearDown
+
+ def tearDown(self):
+ from pyramid import testing
+ testing.setUp = self._orig_setUp
+ testing.tearDown = self._orig_tearDown
+
+ def _callFUT(self, inner, **kw):
+ from pyramid.testing import testConfig
+ with testConfig(**kw) as config:
+ inner(config)
+
+ def test_ok_calls(self):
+ self.assertEqual(self._log, [])
+ def inner(config):
+ self.assertEqual(self._log,
+ [('setUp',
+ {'autocommit': True,
+ 'hook_zca': True,
+ 'registry': None,
+ 'request': None,
+ 'settings': None})])
+ self._log.pop()
+ self._callFUT(inner)
+ self.assertEqual(self._log,
+ [('tearDown', {'unhook_zca': True})])
+
+ def test_teardown_called_on_exception(self):
+ class TestException(Exception):
+ pass
+ def inner(config):
+ self._log = []
+ raise TestException('oops')
+ self.assertRaises(TestException, self._callFUT, inner)
+ self.assertEqual(self._log[0][0], 'tearDown')
+
+ def test_ok_get_config(self):
+ def inner(config):
+ self.assertEqual(config, 'fake config')
+ self._callFUT(inner)
diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py
index 3d85e18f5..785950230 100644
--- a/pyramid/tests/test_util.py
+++ b/pyramid/tests/test_util.py
@@ -288,6 +288,263 @@ class Test_object_description(unittest.TestCase):
self._callFUT(inst),
str(inst)[:100] + ' ... ]')
+class TestTopologicalSorter(unittest.TestCase):
+ def _makeOne(self, *arg, **kw):
+ from pyramid.util import TopologicalSorter
+ return TopologicalSorter(*arg, **kw)
+
+ def test_remove(self):
+ inst = self._makeOne()
+ inst.names.append('name')
+ inst.name2val['name'] = 1
+ inst.req_after.add('name')
+ inst.req_before.add('name')
+ inst.name2after['name'] = ('bob',)
+ inst.name2before['name'] = ('fred',)
+ inst.order.append(('bob', 'name'))
+ inst.order.append(('name', 'fred'))
+ inst.remove('name')
+ self.assertFalse(inst.names)
+ self.assertFalse(inst.req_before)
+ self.assertFalse(inst.req_after)
+ self.assertFalse(inst.name2before)
+ self.assertFalse(inst.name2after)
+ self.assertFalse(inst.name2val)
+ self.assertFalse(inst.order)
+
+ def test_add(self):
+ from pyramid.util import LAST
+ sorter = self._makeOne()
+ sorter.add('name', 'factory')
+ self.assertEqual(sorter.names, ['name'])
+ self.assertEqual(sorter.name2val,
+ {'name':'factory'})
+ self.assertEqual(sorter.order, [('name', LAST)])
+ sorter.add('name2', 'factory2')
+ self.assertEqual(sorter.names, ['name', 'name2'])
+ self.assertEqual(sorter.name2val,
+ {'name':'factory', 'name2':'factory2'})
+ self.assertEqual(sorter.order,
+ [('name', LAST), ('name2', LAST)])
+ sorter.add('name3', 'factory3', before='name2')
+ self.assertEqual(sorter.names,
+ ['name', 'name2', 'name3'])
+ self.assertEqual(sorter.name2val,
+ {'name':'factory', 'name2':'factory2',
+ 'name3':'factory3'})
+ self.assertEqual(sorter.order,
+ [('name', LAST), ('name2', LAST),
+ ('name3', 'name2')])
+
+ def test_sorted_ordering_1(self):
+ sorter = self._makeOne()
+ sorter.add('name1', 'factory1')
+ sorter.add('name2', 'factory2')
+ self.assertEqual(sorter.sorted(),
+ [
+ ('name1', 'factory1'),
+ ('name2', 'factory2'),
+ ])
+
+ def test_sorted_ordering_2(self):
+ from pyramid.util import FIRST
+ sorter = self._makeOne()
+ sorter.add('name1', 'factory1')
+ sorter.add('name2', 'factory2', after=FIRST)
+ self.assertEqual(sorter.sorted(),
+ [
+ ('name2', 'factory2'),
+ ('name1', 'factory1'),
+ ])
+
+ def test_sorted_ordering_3(self):
+ from pyramid.util import FIRST
+ sorter = self._makeOne()
+ add = sorter.add
+ add('auth', 'auth_factory', after='browserid')
+ add('dbt', 'dbt_factory')
+ add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
+ add('browserid', 'browserid_factory')
+ add('txnmgr', 'txnmgr_factory', after='exceptionview')
+ add('exceptionview', 'excview_factory', after=FIRST)
+ self.assertEqual(sorter.sorted(),
+ [
+ ('exceptionview', 'excview_factory'),
+ ('retry', 'retry_factory'),
+ ('txnmgr', 'txnmgr_factory'),
+ ('dbt', 'dbt_factory'),
+ ('browserid', 'browserid_factory'),
+ ('auth', 'auth_factory'),
+ ])
+
+ def test_sorted_ordering_4(self):
+ from pyramid.util import FIRST
+ sorter = self._makeOne()
+ add = sorter.add
+ add('exceptionview', 'excview_factory', after=FIRST)
+ add('auth', 'auth_factory', after='browserid')
+ add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
+ add('browserid', 'browserid_factory')
+ add('txnmgr', 'txnmgr_factory', after='exceptionview')
+ add('dbt', 'dbt_factory')
+ self.assertEqual(sorter.sorted(),
+ [
+ ('exceptionview', 'excview_factory'),
+ ('retry', 'retry_factory'),
+ ('txnmgr', 'txnmgr_factory'),
+ ('browserid', 'browserid_factory'),
+ ('auth', 'auth_factory'),
+ ('dbt', 'dbt_factory'),
+ ])
+
+ def test_sorted_ordering_5(self):
+ from pyramid.util import LAST, FIRST
+ sorter = self._makeOne()
+ add = sorter.add
+ add('exceptionview', 'excview_factory')
+ add('auth', 'auth_factory', after=FIRST)
+ add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
+ add('browserid', 'browserid_factory', after=FIRST)
+ add('txnmgr', 'txnmgr_factory', after='exceptionview', before=LAST)
+ add('dbt', 'dbt_factory')
+ self.assertEqual(sorter.sorted(),
+ [
+ ('browserid', 'browserid_factory'),
+ ('auth', 'auth_factory'),
+ ('exceptionview', 'excview_factory'),
+ ('retry', 'retry_factory'),
+ ('txnmgr', 'txnmgr_factory'),
+ ('dbt', 'dbt_factory'),
+ ])
+
+ def test_sorted_ordering_missing_before_partial(self):
+ from pyramid.exceptions import ConfigurationError
+ sorter = self._makeOne()
+ add = sorter.add
+ add('dbt', 'dbt_factory')
+ add('auth', 'auth_factory', after='browserid')
+ add('retry', 'retry_factory', before='txnmgr', after='exceptionview')
+ add('browserid', 'browserid_factory')
+ self.assertRaises(ConfigurationError, sorter.sorted)
+
+ def test_sorted_ordering_missing_after_partial(self):
+ from pyramid.exceptions import ConfigurationError
+ sorter = self._makeOne()
+ add = sorter.add
+ add('dbt', 'dbt_factory')
+ add('auth', 'auth_factory', after='txnmgr')
+ add('retry', 'retry_factory', before='dbt', after='exceptionview')
+ add('browserid', 'browserid_factory')
+ self.assertRaises(ConfigurationError, sorter.sorted)
+
+ def test_sorted_ordering_missing_before_and_after_partials(self):
+ from pyramid.exceptions import ConfigurationError
+ sorter = self._makeOne()
+ add = sorter.add
+ add('dbt', 'dbt_factory')
+ add('auth', 'auth_factory', after='browserid')
+ add('retry', 'retry_factory', before='foo', after='txnmgr')
+ add('browserid', 'browserid_factory')
+ self.assertRaises(ConfigurationError, sorter.sorted)
+
+ def test_sorted_ordering_missing_before_partial_with_fallback(self):
+ from pyramid.util import LAST
+ sorter = self._makeOne()
+ add = sorter.add
+ add('exceptionview', 'excview_factory', before=LAST)
+ add('auth', 'auth_factory', after='browserid')
+ add('retry', 'retry_factory', before=('txnmgr', LAST),
+ after='exceptionview')
+ add('browserid', 'browserid_factory')
+ add('dbt', 'dbt_factory')
+ self.assertEqual(sorter.sorted(),
+ [
+ ('exceptionview', 'excview_factory'),
+ ('retry', 'retry_factory'),
+ ('browserid', 'browserid_factory'),
+ ('auth', 'auth_factory'),
+ ('dbt', 'dbt_factory'),
+ ])
+
+ def test_sorted_ordering_missing_after_partial_with_fallback(self):
+ from pyramid.util import FIRST
+ sorter = self._makeOne()
+ add = sorter.add
+ add('exceptionview', 'excview_factory', after=FIRST)
+ add('auth', 'auth_factory', after=('txnmgr','browserid'))
+ add('retry', 'retry_factory', after='exceptionview')
+ add('browserid', 'browserid_factory')
+ add('dbt', 'dbt_factory')
+ self.assertEqual(sorter.sorted(),
+ [
+ ('exceptionview', 'excview_factory'),
+ ('retry', 'retry_factory'),
+ ('browserid', 'browserid_factory'),
+ ('auth', 'auth_factory'),
+ ('dbt', 'dbt_factory'),
+ ])
+
+ def test_sorted_ordering_with_partial_fallbacks(self):
+ from pyramid.util import LAST
+ sorter = self._makeOne()
+ add = sorter.add
+ add('exceptionview', 'excview_factory', before=('wontbethere', LAST))
+ add('retry', 'retry_factory', after='exceptionview')
+ add('browserid', 'browserid_factory', before=('wont2', 'exceptionview'))
+ self.assertEqual(sorter.sorted(),
+ [
+ ('browserid', 'browserid_factory'),
+ ('exceptionview', 'excview_factory'),
+ ('retry', 'retry_factory'),
+ ])
+
+ def test_sorted_ordering_with_multiple_matching_fallbacks(self):
+ from pyramid.util import LAST
+ sorter = self._makeOne()
+ add = sorter.add
+ add('exceptionview', 'excview_factory', before=LAST)
+ add('retry', 'retry_factory', after='exceptionview')
+ add('browserid', 'browserid_factory', before=('retry', 'exceptionview'))
+ self.assertEqual(sorter.sorted(),
+ [
+ ('browserid', 'browserid_factory'),
+ ('exceptionview', 'excview_factory'),
+ ('retry', 'retry_factory'),
+ ])
+
+ def test_sorted_ordering_with_missing_fallbacks(self):
+ from pyramid.exceptions import ConfigurationError
+ from pyramid.util import LAST
+ sorter = self._makeOne()
+ add = sorter.add
+ add('exceptionview', 'excview_factory', before=LAST)
+ add('retry', 'retry_factory', after='exceptionview')
+ add('browserid', 'browserid_factory', before=('txnmgr', 'auth'))
+ self.assertRaises(ConfigurationError, sorter.sorted)
+
+ def test_sorted_ordering_conflict_direct(self):
+ from pyramid.exceptions import CyclicDependencyError
+ sorter = self._makeOne()
+ add = sorter.add
+ add('browserid', 'browserid_factory')
+ add('auth', 'auth_factory', before='browserid', after='browserid')
+ self.assertRaises(CyclicDependencyError, sorter.sorted)
+
+ def test_sorted_ordering_conflict_indirect(self):
+ from pyramid.exceptions import CyclicDependencyError
+ sorter = self._makeOne()
+ add = sorter.add
+ add('browserid', 'browserid_factory')
+ add('auth', 'auth_factory', before='browserid')
+ add('dbt', 'dbt_factory', after='browserid', before='auth')
+ self.assertRaises(CyclicDependencyError, sorter.sorted)
+
+class TestSentinel(unittest.TestCase):
+ def test_repr(self):
+ from pyramid.util import Sentinel
+ r = repr(Sentinel('ABC'))
+ self.assertEqual(r, 'ABC')
+
def dummyfunc(): pass
class Dummy(object):
diff --git a/pyramid/tweens.py b/pyramid/tweens.py
index 73a95e1b8..cf2238deb 100644
--- a/pyramid/tweens.py
+++ b/pyramid/tweens.py
@@ -2,6 +2,7 @@ import sys
from pyramid.interfaces import (
IExceptionViewClassifier,
+ IRequest,
IView,
)
@@ -28,7 +29,9 @@ def excview_tween_factory(handler, registry):
# sane (e.g. caching headers)
if 'response' in attrs:
del attrs['response']
- request_iface = attrs['request_iface']
+ # we use .get instead of .__getitem__ below due to
+ # https://github.com/Pylons/pyramid/issues/700
+ request_iface = attrs.get('request_iface', IRequest)
provides = providedBy(exc)
for_ = (IExceptionViewClassifier, request_iface.combined, provides)
view_callable = adapters.lookup(for_, IView, default=None)
diff --git a/pyramid/util.py b/pyramid/util.py
index 6190e8156..d83837322 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -1,8 +1,14 @@
import inspect
import weakref
+from pyramid.exceptions import (
+ ConfigurationError,
+ CyclicDependencyError,
+ )
+
from pyramid.compat import (
iteritems_,
+ is_nonstr_iter,
integer_types,
string_types,
text_,
@@ -288,3 +294,162 @@ def shortrepr(object, closer):
r = r[:100] + ' ... %s' % closer
return r
+class Sentinel(object):
+ def __init__(self, repr):
+ self.repr = repr
+
+ def __repr__(self):
+ return self.repr
+
+FIRST = Sentinel('FIRST')
+LAST = Sentinel('LAST')
+
+class TopologicalSorter(object):
+ """ A utility class which can be used to perform topological sorts against
+ tuple-like data."""
+ def __init__(
+ self,
+ default_before=LAST,
+ default_after=None,
+ first=FIRST,
+ last=LAST,
+ ):
+ self.names = []
+ self.req_before = set()
+ self.req_after = set()
+ self.name2before = {}
+ self.name2after = {}
+ self.name2val = {}
+ self.order = []
+ self.default_before = default_before
+ self.default_after = default_after
+ self.first = first
+ self.last = last
+
+ def remove(self, name):
+ """ Remove a node from the sort input """
+ self.names.remove(name)
+ del self.name2val[name]
+ after = self.name2after.pop(name, [])
+ if after:
+ self.req_after.remove(name)
+ for u in after:
+ self.order.remove((u, name))
+ before = self.name2before.pop(name, [])
+ if before:
+ self.req_before.remove(name)
+ for u in before:
+ self.order.remove((name, u))
+
+ def add(self, name, val, after=None, before=None):
+ """ Add a node to the sort input. The ``name`` should be a string or
+ any other hashable object, the ``val`` should be the sortable (doesn't
+ need to be hashable). ``after`` and ``before`` represents the name of
+ one of the other sortables (or a sequence of such named) or one of the
+ special sentinel values :attr:`pyramid.util.FIRST`` or
+ :attr:`pyramid.util.LAST` representing the first or last positions
+ respectively. ``FIRST`` and ``LAST`` can also be part of a sequence
+ passed as ``before`` or ``after``. A sortable should not be added
+ after LAST or before FIRST. An example::
+
+ sorter = TopologicalSorter()
+ sorter.add('a', {'a':1}, before=LAST, after='b')
+ sorter.add('b', {'b':2}, before=LAST, after='c')
+ sorter.add('c', {'c':3})
+
+ sorter.sorted() # will be {'c':3}, {'b':2}, {'a':1}
+
+ """
+ if name in self.names:
+ self.remove(name)
+ self.names.append(name)
+ self.name2val[name] = val
+ if after is None and before is None:
+ before = self.default_before
+ after = self.default_after
+ if after is not None:
+ if not is_nonstr_iter(after):
+ after = (after,)
+ self.name2after[name] = after
+ self.order += [(u, name) for u in after]
+ self.req_after.add(name)
+ if before is not None:
+ if not is_nonstr_iter(before):
+ before = (before,)
+ self.name2before[name] = before
+ self.order += [(name, o) for o in before]
+ self.req_before.add(name)
+
+
+ def sorted(self):
+ """ Returns the sort input values in topologically sorted order"""
+ order = [(self.first, self.last)]
+ roots = []
+ graph = {}
+ names = [self.first, self.last]
+ names.extend(self.names)
+
+ for a, b in self.order:
+ order.append((a, b))
+
+ def add_node(node):
+ if not node in graph:
+ 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)
+
+ for name in names:
+ add_node(name)
+
+ has_before, has_after = set(), set()
+ for a, b in order:
+ if a in names and b in names: # deal with missing dependencies
+ add_arc(a, b)
+ has_before.add(a)
+ has_after.add(b)
+
+ if not self.req_before.issubset(has_before):
+ raise ConfigurationError(
+ 'Unsatisfied before dependencies: %s'
+ % (', '.join(sorted(self.req_before - has_before)))
+ )
+ if not self.req_after.issubset(has_after):
+ raise ConfigurationError(
+ 'Unsatisfied after dependencies: %s'
+ % (', '.join(sorted(self.req_after - has_after)))
+ )
+
+ sorted_names = []
+
+ while roots:
+ root = roots.pop(0)
+ sorted_names.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 name in sorted_names:
+ if name in self.names:
+ result.append((name, self.name2val[name]))
+
+ return result
+
diff --git a/pyramid/view.py b/pyramid/view.py
index 12a2efde6..51ded423c 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -170,7 +170,7 @@ class view_config(object):
``request_type``, ``route_name``, ``request_method``, ``request_param``,
``containment``, ``xhr``, ``accept``, ``header``, ``path_info``,
``custom_predicates``, ``decorator``, ``mapper``, ``http_cache``,
- ``match_param``, and ``predicates``.
+ ``match_param``, ``csrf_token``, ``physical_path``, and ``predicates``.
The meanings of these arguments are the same as the arguments passed to
:meth:`pyramid.config.Configurator.add_view`. If any argument is left
diff --git a/setup.py b/setup.py
index 03ebb4293..4ea63a3ee 100644
--- a/setup.py
+++ b/setup.py
@@ -68,7 +68,7 @@ testing_extras = tests_require + [
]
setup(name='pyramid',
- version='1.4dev',
+ version='1.4a3',
description=('The Pyramid web application development framework, a '
'Pylons project'),
long_description=README + '\n\n' + CHANGES,
@@ -79,6 +79,7 @@ setup(name='pyramid',
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.2",
+ "Programming Language :: Python :: 3.3",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Framework :: Pyramid",