summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAtsushi Odagiri <aodagx@gmail.com>2010-11-27 15:08:31 +0900
committerAtsushi Odagiri <aodagx@gmail.com>2010-11-27 15:08:31 +0900
commitcd2f78c893ce6ae98004189a1a0a649fac96d033 (patch)
tree5ab84f72e6bf0476d7d40841f3341aaf9a4e54a4
parentba32a8d0dcee7f3cef231e08ed60d52fbf60ed4f (diff)
parent4d76ed6f46304d43a6b95da7a4d5388527bf6c1e (diff)
downloadpyramid-cd2f78c893ce6ae98004189a1a0a649fac96d033.tar.gz
pyramid-cd2f78c893ce6ae98004189a1a0a649fac96d033.tar.bz2
pyramid-cd2f78c893ce6ae98004189a1a0a649fac96d033.zip
Merge remote branch 'remotes/upstream/master'
-rw-r--r--.gitignore10
-rw-r--r--CHANGES.txt173
-rw-r--r--TODO.txt149
-rw-r--r--docs/Makefile7
-rw-r--r--docs/_static/pylons.png (renamed from docs/_static/pyramid.png)bin4063 -> 4063 bytes
-rw-r--r--docs/_static/pylons_small.pngbin0 -> 4063 bytes
-rw-r--r--docs/api/interfaces.rst1
-rw-r--r--docs/api/url.rst2
-rw-r--r--docs/conf.py11
-rw-r--r--docs/copyright.rst3
-rw-r--r--docs/designdefense.rst37
-rw-r--r--docs/index.rst52
-rw-r--r--docs/latexindex.rst1
-rw-r--r--docs/narr/MyProject/development.ini26
-rw-r--r--docs/narr/MyProject/myproject/__init__.py3
-rw-r--r--docs/narr/configuration.rst18
-rw-r--r--docs/narr/contextfinding.rst2
-rw-r--r--docs/narr/declarative.rst2
-rw-r--r--docs/narr/environment.rst66
-rw-r--r--docs/narr/firstapp.rst49
-rw-r--r--docs/narr/handlers.rst14
-rw-r--r--docs/narr/hybrid.rst32
-rw-r--r--docs/narr/i18n.rst9
-rw-r--r--docs/narr/introduction.rst2
-rw-r--r--docs/narr/project.rst19
-rw-r--r--docs/narr/startup.rst6
-rw-r--r--docs/narr/static.rst31
-rw-r--r--docs/narr/unittesting.rst2
-rw-r--r--docs/narr/urldispatch.rst159
-rw-r--r--docs/tutorials/gae/index.rst4
-rw-r--r--docs/tutorials/wiki/basiclayout.rst9
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki2/basiclayout.rst47
-rw-r--r--docs/tutorials/wiki2/definingviews.rst8
-rw-r--r--docs/tutorials/wiki2/src/authorization/development.ini37
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/__init__.py17
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models.py4
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/tests.py8
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/development.ini37
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py11
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/models.py4
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py3
-rw-r--r--docs/tutorials/wiki2/src/models/development.ini37
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/__init__.py11
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/models.py4
-rw-r--r--docs/tutorials/wiki2/src/views/development.ini37
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/__init__.py17
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/models.py4
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/tests.py8
-rw-r--r--docs/tutorials/zodbsessions/index.rst189
-rw-r--r--docs/zcml/route.rst6
-rw-r--r--pyramid/authentication.py6
-rw-r--r--pyramid/chameleon_text.py51
-rw-r--r--pyramid/chameleon_zpt.py53
-rw-r--r--pyramid/compat/__init__.py7
-rw-r--r--pyramid/configuration.py233
-rw-r--r--pyramid/interfaces.py49
-rw-r--r--pyramid/mako_templating.py50
-rwxr-xr-xpyramid/paster_templates/alchemy/+package+/__init__.py_tmpl11
-rwxr-xr-xpyramid/paster_templates/alchemy/+package+/models.py7
-rw-r--r--pyramid/paster_templates/alchemy/development.ini_tmpl37
-rw-r--r--pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl4
-rw-r--r--pyramid/paster_templates/pylons_basic/development.ini_tmpl31
-rw-r--r--pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl4
-rw-r--r--pyramid/paster_templates/pylons_minimal/development.ini_tmpl26
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl17
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/models.py3
-rw-r--r--pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl3
-rw-r--r--pyramid/paster_templates/pylons_sqla/development.ini_tmpl39
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl11
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/models.py4
-rw-r--r--pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl5
-rw-r--r--pyramid/paster_templates/routesalchemy/development.ini_tmpl43
-rw-r--r--pyramid/paster_templates/starter/+package+/__init__.py_tmpl2
-rw-r--r--pyramid/paster_templates/starter/+package+/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/static/ie6.css8
-rw-r--r--pyramid/paster_templates/starter/+package+/static/logo.pngbin6641 -> 0 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/static/pylons.css47
-rw-r--r--pyramid/paster_templates/starter/+package+/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--pyramid/paster_templates/starter/+package+/templates/mytemplate.pt25
-rw-r--r--pyramid/paster_templates/starter/development.ini_tmpl26
-rw-r--r--pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl2
-rw-r--r--pyramid/paster_templates/starter_zcml/development.ini_tmpl26
-rw-r--r--pyramid/paster_templates/zodb/+package+/__init__.py_tmpl2
-rw-r--r--pyramid/paster_templates/zodb/development.ini_tmpl26
-rw-r--r--pyramid/path.py3
-rw-r--r--pyramid/renderers.py150
-rw-r--r--pyramid/request.py73
-rw-r--r--pyramid/settings.py13
-rw-r--r--pyramid/testing.py57
-rw-r--r--pyramid/tests/test_authentication.py6
-rw-r--r--pyramid/tests/test_chameleon_text.py71
-rw-r--r--pyramid/tests/test_chameleon_zpt.py83
-rw-r--r--pyramid/tests/test_configuration.py236
-rw-r--r--pyramid/tests/test_integration.py301
-rw-r--r--pyramid/tests/test_mako_templating.py164
-rw-r--r--pyramid/tests/test_renderers.py38
-rw-r--r--pyramid/tests/test_request.py43
-rw-r--r--pyramid/tests/test_settings.py4
-rw-r--r--pyramid/tests/test_testing.py24
-rw-r--r--pyramid/tests/test_traversal.py26
-rw-r--r--pyramid/tests/test_url.py21
-rw-r--r--pyramid/tests/test_urldispatch.py8
-rw-r--r--pyramid/tests/test_util.py177
-rw-r--r--pyramid/tests/test_zcml.py4
-rw-r--r--pyramid/tests/viewdecoratorapp/views/templates/foo.mak (renamed from pyramid/tests/viewdecoratorapp/views/templates/foo.pt)0
-rw-r--r--pyramid/tests/viewdecoratorapp/views/views.py10
-rw-r--r--pyramid/traversal.py13
-rw-r--r--pyramid/url.py40
-rw-r--r--pyramid/urldispatch.py20
-rw-r--r--pyramid/util.py144
-rw-r--r--pyramid/view.py2
-rw-r--r--pyramid/zcml.py12
-rw-r--r--setup.py8
121 files changed, 2493 insertions, 1514 deletions
diff --git a/.gitignore b/.gitignore
index 09439306e..ae0b17b8e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,15 @@
*.egg
*.egg-info
*.pyc
+*$py.class
*.pt.py
*.txt.py
.coverage
-env26
-env24
-env27
+tutorial.db
+env26/
+env24/
+env27/
+jyenv/
+pypyenv/
build/
dist/
diff --git a/CHANGES.txt b/CHANGES.txt
index e7c42d659..1675ab9fc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,6 +4,165 @@ Next release
Features
--------
+- Add logging configuration to all paster templates.
+
+- ``pyramid_alchemy``, ``pyramid_routesalchemy``, and ``pylons_sqla`` paster
+ templates now use idiomatic SQLAlchemy configuration in their respective
+ ``.ini`` files and Python code.
+
+- ``pyramid.testing.DummyRequest`` now has a class variable,
+ ``query_string``, which defaults to the empty string.
+
+- Add support for json on GAE by catching NotImplementedError and importing
+ simplejson from django.utils.
+
+- The Mako renderer now accepts a resource specification for
+ ``mako.module_directory``.
+
+- New boolean Mako settings variable ``mako.strict_undefined``. See `Mako
+ Context Variables
+ <http://www.makotemplates.org/docs/runtime.html#context-variables>`_ for
+ its meaning.
+
+Dependencies
+------------
+
+- Depend on Mako 0.3.6+ (we now require the ``strict_undefined`` feature).
+
+Bug Fixes
+---------
+
+- The ``pyramid_routesalchemy`` paster template's unit tests failed
+ (``AssertionError: 'SomeProject' != 'someproject'``). This is fixed.
+
+- Make default renderer work (renderer factory registered with no name, which
+ is active for every view unless the view names a specific renderer).
+
+- The Mako renderer did not properly turn the ``mako.imports``,
+ ``mako.default_filters``, and ``mako.imports`` settings into lists.
+
+- The Mako renderer did not properly convert the ``mako.error_handler``
+ setting from a dotted name to a callable.
+
+Documentation
+-------------
+
+- "Sample Applications" section of docs changed to note existence of Cluegun,
+ Shootout and Virginia sample applications, ported from their repoze.bfg
+ origin packages.
+
+- SQLAlchemy+URLDispatch tutorial updated to integrate changes to
+ ``pyramid_routesalchemy`` template.
+
+- Add ``pyramid.interfaces.ITemplateRenderer`` interface to Interfaces API
+ chapter (has ``implementation()`` method, required to be used when getting
+ at Chameleon macros).
+
+
+1.0a4 (2010-11-21)
+==================
+
+Features
+--------
+
+- URL Dispatch now allows for replacement markers to be located anywhere
+ in the pattern, instead of immediately following a ``/``.
+
+- URL Dispatch now uses the form ``{marker}`` to denote a replace marker in
+ the route pattern instead of ``:marker``. The old colon-style marker syntax
+ is still accepted for backwards compatibility. The new format allows a
+ regular expression for that marker location to be used instead of the
+ default ``[^/]+``, for example ``{marker:\d+}`` is now valid to require the
+ marker to be digits.
+
+- Add a ``pyramid.url.route_path`` API, allowing folks to generate relative
+ URLs. Calling ``route_path`` is the same as calling
+ ``pyramid.url.route_url`` with the argument ``_app_url`` equal to the empty
+ string.
+
+- Add a ``pyramid.request.Request.route_path`` API. This is a convenience
+ method of the request which calls ``pyramid.url.route_url``.
+
+- Make test suite pass on Jython (requires PasteScript trunk, presumably to
+ be 1.7.4).
+
+- Make test suite pass on PyPy (Chameleon doesn't work).
+
+- Surrounding application configuration with ``config.begin()`` and
+ ``config.end()`` is no longer necessary. All paster templates have been
+ changed to no longer call these functions.
+
+- Fix configurator to not convert ``ImportError`` to ``ConfigurationError``
+ if the import that failed was unrelated to the import requested via a
+ dotted name when resolving dotted names (such as view dotted names).
+
+Documentation
+-------------
+
+- SQLAlchemy+URLDispatch and ZODB+Traversal tutorials have been updated to
+ not call ``config.begin()`` or ``config.end()``.
+
+Bug Fixes
+---------
+
+- Add deprecation warnings to import of ``pyramid.chameleon_text`` and
+ ``pyramid.chameleon_zpt`` of ``get_renderer``, ``get_template``,
+ ``render_template``, and ``render_template_to_response``.
+
+- Add deprecation warning for import of ``pyramid.zcml.zcml_configure`` and
+ ``pyramid.zcml.file_configure``.
+
+- The ``pyramid_alchemy`` paster template had a typo, preventing an import
+ from working.
+
+- Fix apparent failures when calling ``pyramid.traversal.find_model(root,
+ path)`` or ``pyramid.traversal.traverse(path)`` when ``path`` is
+ (erroneously) a Unicode object. The user is meant to pass these APIs a
+ string object, never a Unicode object. In practice, however, users indeed
+ pass Unicode. Because the string that is passed must be ASCII encodeable,
+ now, if they pass a Unicode object, its data is eagerly converted to an
+ ASCII string rather than being passed along to downstream code as a
+ convenience to the user and to prevent puzzling second-order failures from
+ cropping up (all failures will occur within ``pyramid.traversal.traverse``
+ rather than later down the line as the result of calling e.g.
+ ``traversal_path``).
+
+Backwards Incompatibilities
+---------------------------
+
+- The ``pyramid.testing.zcml_configure`` API has been removed. It had been
+ advertised as removed since repoze.bfg 1.2a1, but hadn't actually been.
+
+Deprecations
+------------
+
+- The ``pyramid.settings.get_settings`` API is now deprecated. Use
+ ``pyramid.threadlocals.get_current_registry().settings`` instead or use the
+ ``settings`` attribute of the registry available from the request
+ (``request.registry.settings``).
+
+Documentation
+-------------
+
+- Removed ``zodbsessions`` tutorial chapter. It's still useful, but we now
+ have a SessionFactory abstraction which competes with it, and maintaining
+ documentation on both ways to do it is a distraction.
+
+Internal
+--------
+
+- Replace Twill with WebTest in internal integration tests (avoid deprecation
+ warnings generated by Twill).
+
+1.0a3 (2010-11-16)
+==================
+
+Features
+--------
+
+- Added Mako TemplateLookup settings for ``mako.error_handler``,
+ ``mako.default_filters``, and ``mako.imports``.
+
- Normalized all paster templates: each now uses the name ``main`` to
represent the function that returns a WSGI application, each now uses
WebError, each now has roughly the same shape of development.ini style.
@@ -13,9 +172,9 @@ Features
- New API method: ``pyramid.settings.asbool``.
-- New API methods for ``pyramid.request.Request``: ``model_url`` and
- ``route_url``. These are simple passthroughs for their respective
- functions in ``pyramid.url``.
+- New API methods for ``pyramid.request.Request``: ``model_url``,
+ ``route_url``, and ``static_url``. These are simple passthroughs for their
+ respective functions in ``pyramid.url``.
- The ``settings`` object which used to be available only when
``request.settings.get_settings`` was called is now available as
@@ -68,6 +227,14 @@ Behavior Differences
a ``registry`` attribute on the ZCML context (kill off use of
threadlocals).
+- Internal: Chameleon template renderers now accept two arguments: ``path``
+ and ``lookup``. ``Lookup`` will be an instance of a lookup class which
+ supplies (late-bound) arguments for debug, reload, and translate. Any
+ third-party renderers which use (the non-API) function
+ ``pyramid.renderers.template_renderer_factory`` will need to adjust their
+ implementations to obey the new callback argument list. This change was to
+ kill off inappropriate use of threadlocals.
+
1.0a2 (2010-11-09)
==================
diff --git a/TODO.txt b/TODO.txt
index 70d760f45..0033eb3b3 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -1,5 +1,56 @@
-:mod:`repoze.bfg` TODOs
-=======================
+Pyramid TODOs
+=============
+
+Must-Have (before 1.0)
+----------------------
+
+- Add a ``handler`` ZCML directive. This implies some slightly dicey
+ refactoring of the configurator to allow it to generate ZCML
+ "discriminators" for views and routes, that could be implemented in terms
+ of "twophase configuration" in "should have" below.
+
+- Provide a .flash API on session object.
+
+- Use ``@register_view`` instead of ``@view_config`` and change view docs to
+ use "view registration" instead of "view configuration".
+
+Should-Have
+-----------
+
+- Add docs for httpexceptions module for each webob.exc class that inherits
+ from WSGIHTTPException.
+
+- Remove "BFG" from Pyramid-specific environ variables.
+
+- translationdir ZCML directive use of ``path_spec`` should maybe die.
+
+- Add CRSF token creation/checking machinery (only "should have" vs. "must
+ have" because I'm not sure it belongs in Pyramid.. it definitely must exist
+ in formgen libraries, and *might* belong in Pyramid).
+
+- Change "Cleaning up After a Request" in the urldispatch chapter to
+ use ``request.add_response_callback``.
+
+- Twophase configuration (config = Configurator(autocommit=False)). Maybe
+ use ``zope.configuration`` ConfigurationContext as config.registry.ctx and
+ push execution into the configurator.
+
+Nice-to-Have
+------------
+
+- Try to make test suite pass on IronPython.
+
+- Non-bwcompat use of threadlocals that need to be documented or ameliorated:
+
+ security.principals_allowed_by_permission
+
+ resource.OverrideProvider._get_overrides: can't credibly be removed,
+ because it stores an overrideprovider as a module-scope global.
+
+ traversal.traverse: this API is a stepchild, and needs to be changed.
+
+ Configurator.add_translation_dirs: not passed any context but a message,
+ can't credibly be removed.
- Supply ``X-Vhm-Host`` support.
@@ -9,7 +60,8 @@
- Change docs about creating a venusian decorator to not use ZCA.
-- ``decorator=`` parameter to bfg_view.
+- ``decorator=`` parameter to view_config. This would replace the existing
+ _map_view "decorator" if it existed.
- Try to better explain the relationship between a renderer and a
template in the templates chapter and elsewhere. Scan the
@@ -44,98 +96,15 @@
action = '^foo$'
mypackage.views.MyView.foo_GET
-- Ability to use configurator as a context manager.
-
- Provide a response_cookies attribute on the request for rendered
responses that can be used as input to response.set_cookie.
- Raise an exception when a value in response_headerlist is not a
string or decide to encode.
-- Change "Cleaning up After a Request" in the urldispatch chapter to
- use ``request.add_response_callback``.
-
-- Update App engine chapter.
-
-- Browser id?
-
-- .flash API on session.
-
-- CRSF token machinery
-
-- ``add_handler`` documentation.
-
-- ``handler`` ZCML directive.
-
-- ``docs`` directory for each paster template.
-
-- "BFG" in environ variables.
-
-- Test on GAE, Jython, PyPy, IronPython.
-
-- Add docs for httpexceptions.
-
-- RendererHelper -> RendererInfo?
-
-- Do something about ZODB session chapter: either remove or create a
- pyramid_zodbsessions package.
-
-- translationdir ZCML directive use of ``path_spec`` should maybe die.
-
-- Option for route_url to omit the host and port (perhaps a different
- function named ``route_path``).
-
-- SQLAlchemy idiomatics:
-
- <RaFromBRC> mcdonc: those paster templates all look pretty good... the
- only thing i'd consider is adjusting your config variable names to match
- exactly what sqlalchemy uses as parameter names, see here:
- http://www.sqlalchemy.org/docs/core/engines.html
-
- <RaFromBRC> mcdonc: especially in the pylons_sqla ini file, where the db
- initialization is mixed in w/ the app config...
-
- <RaFromBRC> ... i'd use "sqlalchemy.PARAMETER" for all of the sqla
- settings, so it could easily be handed to engine_from_config w/o any need
- to parse by hand
-
- <RaFromBRC> mcdonc: in the other ini files, where sqlalchemy is given its
- own part, the "sqlalchemy." prefix probably isn't necessary, but matching
- the parameter names (e.g. 'url' instead of 'db_string') is still probably
- a good idea
-
-- Default renderer.
-
-- Non-bwcompat use of threadlocals:
-
- security.principals_allowed_by_permission
-
- chameleon_text.TextTemplateRenderer.template (also uses get_settings)
-
- chameleon_zpt.ZPTTemplateRenderer.template (also uses get_settings)
-
- resource.OverrideProvider._get_overrides: can't credibly be removed,
- because it stores an overrideprovider as a module-scope global.
-
- traversal.traverse: this API is a stepchild, and needs to be changed.
-
- Configurator.add_translation_dirs: not passed any context but a message,
- can't credibly be removed.
-
-- Add deprecation warnings for:
-
- - Use of chameleon_zpt and chameleon_text templating functions (use
- renderer API instead).
-
- - settings.get_settings
-
- - zcml.zcml_configure
-
- - zcml.file_configure
-
-- Add static_url as method of request.
-
-
-
+- Update App engine chapter with less creaky directions.
+- Add functionality that mocks the behavior of ``repoze.browserid``.
+- One way to split up views chapter: views with renderers / views without
+ renderers.
diff --git a/docs/Makefile b/docs/Makefile
index 768efb9df..b74c55bd5 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -25,7 +25,7 @@ help:
clean:
-rm -rf _build/*
-html:
+html: _themes/
mkdir -p _build/html _build/doctrees
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
@echo
@@ -47,7 +47,7 @@ pickle:
web: pickle
-htmlhelp:
+htmlhelp: _themes
mkdir -p _build/htmlhelp _build/doctrees
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
@echo
@@ -83,3 +83,6 @@ epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) _build/epub
@echo
@echo "Build finished. The epub file is in _build/epub."
+
+_themes:
+ git clone git://github.com/Pylons/pylons_sphinx_theme.git _themes
diff --git a/docs/_static/pyramid.png b/docs/_static/pylons.png
index 53f83e4d0..53f83e4d0 100644
--- a/docs/_static/pyramid.png
+++ b/docs/_static/pylons.png
Binary files differ
diff --git a/docs/_static/pylons_small.png b/docs/_static/pylons_small.png
new file mode 100644
index 000000000..53f83e4d0
--- /dev/null
+++ b/docs/_static/pylons_small.png
Binary files differ
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index b27428d89..b3c14e5f7 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -33,4 +33,5 @@ Other Interfaces
.. autointerface:: IRendererInfo
+ .. autointerface:: ITemplateRenderer
diff --git a/docs/api/url.rst b/docs/api/url.rst
index 71987498a..8c702a3fb 100644
--- a/docs/api/url.rst
+++ b/docs/api/url.rst
@@ -9,6 +9,8 @@
.. autofunction:: route_url
+ .. autofunction:: route_path
+
.. autofunction:: static_url
.. autofunction:: urlencode
diff --git a/docs/conf.py b/docs/conf.py
index c2ecb1e8d..653f7ab15 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -13,6 +13,9 @@
import sys, os
import datetime
+import warnings
+
+warnings.simplefilter('ignore', DeprecationWarning)
# skip raw nodes
from sphinx.writers.text import TextTranslator
@@ -73,7 +76,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year
# other places throughout the built documents.
#
# The short X.Y version.
-version = '1.0a2'
+version = '1.0a4'
# The full version, including alpha/beta/rc tags.
release = version
@@ -125,11 +128,11 @@ html_theme = 'pylons'
html_title = 'The Pyramid Web Application Development Framework v%s' % release
# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
+html_short_title = 'Home'
# The name of an image file (within the static path) to place at the top of
# the sidebar.
-#html_logo = '_static/pylons.png'
+html_logo = '_static/pyramid.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
@@ -198,7 +201,7 @@ latex_documents = [
# The name of an image file (relative to this directory) to place at the top of
# the title page.
-latex_logo = '_static/pyramid.png'
+latex_logo = '_static/pylons_small.png'
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
diff --git a/docs/copyright.rst b/docs/copyright.rst
index 64a0f819b..fa564a785 100644
--- a/docs/copyright.rst
+++ b/docs/copyright.rst
@@ -49,7 +49,8 @@ Attributions
------------
Contributors:
- Ben Bangert, Blaise Laflamme, Carlos de la Guardia, Paul Everitt
+ Ben Bangert, Blaise Laflamme, Carlos de la Guardia, Paul Everitt,
+ Marius Gedminas
.. Cover Designer:
.. Nat Hardwick of `Electrosoup <http://www.electrosoup.co.uk>`_.
diff --git a/docs/designdefense.rst b/docs/designdefense.rst
index 92facf13c..e3a816269 100644
--- a/docs/designdefense.rst
+++ b/docs/designdefense.rst
@@ -40,9 +40,7 @@ Too Complex
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.add_view(hello_world)
- config.end()
app = config.make_wsgi_app()
serve(app)
@@ -549,9 +547,7 @@ everything done completely imperatively. For example, the very most basic
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.add_view(hello_world)
- config.end()
app = config.make_wsgi_app()
serve(app)
@@ -1674,37 +1670,6 @@ can interface with a WSGI application is placed on the server
developer, not the web framework developer, making it more likely to
be timely and correct.
-:meth:`pyramid.configuration.Configurator.begin` and :meth:`pyramid.configuration.Configurator.end` methods
-+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
-The methods :meth:`pyramid.configuration.Configurator.begin` and
-:meth:`pyramid.configuration.Configurator.end` are used to bracket
-the configuration phase of a :app:`Pyramid` application.
-
-These exist because existing legacy third party *configuration* (not
-runtime) code relies on a threadlocal stack being populated. The
-``begin`` method pushes data on to a threadlocal stack. The ``end``
-method pops it back off.
-
-For the simplest applications, these lines are actually not required.
-I *could* omit them from every Pyramid hello world app without ill
-effect. However, when users use certain configuration methods (ones
-not represented in the hello world app), calling code will begin to
-fail when it is not bracketed between a ``begin()`` and an ``end()``.
-It is just easier to tell users that this bracketing is required than
-to try to explain to them which circumstances it is actually required
-and which it is not, because the explanation is often torturous.
-
-The effectively-required execution of these two methods is a wholly
-bogus artifact of an early bad design decision which encouraged
-application developers to use threadlocal data structures during the
-execution of configuration plugins. However, I don't hate my
-framework's users enough to break backwards compatibility for the sake
-of removing two boilerplate lines of code, so it stays, at least for
-the foreseeable future. If I eventually figure out a way to remove
-the requirement, these methods will turn into no-ops and they will be
-removed from the documenation.
-
Wrapping Up
+++++++++++
@@ -1724,9 +1689,7 @@ where comments take into account what we've discussed in the
if __name__ == '__main__':
from pyramid.configuration import Configurator
config = Configurator() # no global application object.
- config.begin() # bogus, but required.
config.add_view(hello_world) # explicit non-decorator registration
- config.end() # bogus, but required.
app = config.make_wsgi_app() # explicitly WSGI
serve(app, host='0.0.0.0') # explicitly WSGI
diff --git a/docs/index.rst b/docs/index.rst
index 4efb25dde..72c21bbc8 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -80,7 +80,6 @@ applications to various platforms.
tutorials/gae/index.rst
tutorials/modwsgi/index.rst
tutorials/zeo/index.rst
- tutorials/zodbsessions/index.rst
tutorials/catalog/index.rst
Reference Material
@@ -114,38 +113,39 @@ Design Documentation
Sample Applications
===================
-.. warning::
+`cluegun <https://github.com/Pylons/cluegun>`_ is a simple pastebin
+application based on Rocky Burt's `ClueBin
+<http://pypi.python.org/pypi/ClueBin/0.2.3>`_. It demonstrates form
+processing, security, and the use of :term:`ZODB` within a :app:`Pyramid`
+application. Check this application out via::
- These applications are for an older version of :app:`Pyramid`,
- which was named :mod:`repoze.bfg`. We'll be updating them soon to
- use :app:`Pyramid`.
+ git clone git://github.com/Pylons/cluegun.git
-`repoze.cluegun <http://svn.repoze.org/repoze.cluegun/trunk/>`_ is a
-simple pastebin application based on Rocky Burt's `ClueBin
-<http://pypi.python.org/pypi/ClueBin/0.2.3>`_. It demonstrates form
-processing, security, and the use of :term:`ZODB` within a
-:mod:`repoze.bfg` application. It also has very simple
-:term:`repoze.who` integration. Check this application out of
-Subversion via::
+`virginia <https://github.com/Pylons/virginia>`_ is a very simple dynamic
+file rendering application. It is willing to render structured text
+documents, HTML documents, and images from a filesystem directory. An
+earlier version of this application runs the `repoze.org
+<http://repoze.org>`_ website. Check this application out via::
- svn co http://svn.repoze.org/repoze.cluegun/trunk repoze.cluegun
+ git clone git://github.com/Pylons/virginia.git
-`repoze.virginia <http://svn.repoze.org/repoze.virginia/trunk/>`_ is a
-very simple dynamic file rendering application. It is willing to
-render structured text documents, HTML documents, and images from a
-filesystem directory. This application runs the `repoze.org
-<http://repoze.org>`_ website. Check this application out of
-Subversion via::
+`shootout <https://github.com/Pylons/shootout>`_ is an example "idea
+competition" application by Carlos de la Guardia. It demonstrates a hybrid
+of :term:`URL dispatch` and :term:`traversal` and integration with
+`SQLAlchemy <http://www.sqlalchemy.org/>`_, :term:`repoze.who`, and
+`Deliverance <http://www.deliveranceproject.org/>`_. Check this application
+out of version control via::
- svn co http://svn.repoze.org/repoze.virginia/trunk repoze.virginia
+ git clone git://github.com/Pylons/shootout.git
-`repoze.shootout <http://svn.repoze.org/repoze.shootout/trunk/>`_ is
-an example "idea competition" application by Carlos de la Guardia. It
-demonstrates a hybrid of :term:`URL dispatch` and :term:`traversal`
-and integration with `SQLAlchemy <http://www.sqlalchemy.org/>`_ and
-:term:`repoze.who`. Check this application out of Subversion via::
+Older Sample Applications (repoze.bfg)
+======================================
+
+.. note::
- svn co http://svn.repoze.org/repoze.shootout/trunk repoze.shootout
+ These applications are for an older version of :app:`Pyramid`, which was
+ named :mod:`repoze.bfg`. They won't work unmodified under Pyramid, but
+ might provide useful clues.
`bfgsite <http://svn.repoze.org/bfgsite/trunk>`_ is the software which
runs the `bfg.repoze.org <http://bfg.repoze.org>`_ website. It
diff --git a/docs/latexindex.rst b/docs/latexindex.rst
index 4efb193bd..388297de7 100644
--- a/docs/latexindex.rst
+++ b/docs/latexindex.rst
@@ -71,7 +71,6 @@ Tutorials
tutorials/gae/index.rst
tutorials/modwsgi/index.rst
tutorials/zeo/index.rst
- tutorials/zodbsessions/index.rst
tutorials/catalog/index.rst
.. _api_reference:
diff --git a/docs/narr/MyProject/development.ini b/docs/narr/MyProject/development.ini
index 9c51cfe6e..80d89e46a 100644
--- a/docs/narr/MyProject/development.ini
+++ b/docs/narr/MyProject/development.ini
@@ -15,3 +15,29 @@ pipeline =
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/docs/narr/MyProject/myproject/__init__.py b/docs/narr/MyProject/myproject/__init__.py
index bfbbfd4df..580dfe546 100644
--- a/docs/narr/MyProject/myproject/__init__.py
+++ b/docs/narr/MyProject/myproject/__init__.py
@@ -5,11 +5,8 @@ def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(root_factory=get_root, settings=settings)
- config.begin()
config.add_view('myproject.views.my_view',
context='myproject.models.MyModel',
renderer='myproject:templates/mytemplate.pt')
config.add_static_view('static', 'myproject:static')
- config.end()
return config.make_wsgi_app()
-
diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst
index 6a91cbf75..ae02a5a6c 100644
--- a/docs/narr/configuration.rst
+++ b/docs/narr/configuration.rst
@@ -47,20 +47,16 @@ imperatively:
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.add_view(hello_world)
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
-We won't talk much about what this application does yet. Just note
-that the "configuration' statements take place underneath the ``if
-__name__ == '__main__':`` stanza in the form of method calls on a
-:term:`Configurator` object (e.g. ``config.begin()``,
-``config.add_view(...)``, and ``config.end()``. These statements take
-place one after the other, and are executed in order, so the full
-power of Python, including conditionals, can be employed in this mode
-of configuration.
+We won't talk much about what this application does yet. Just note that the
+"configuration' statements take place underneath the ``if __name__ ==
+'__main__':`` stanza in the form of method calls on a :term:`Configurator`
+object (e.g. ``config.add_view(...)``). These statements take place one
+after the other, and are executed in order, so the full power of Python,
+including conditionals, can be employed in this mode of configuration.
.. index::
single: view_config
@@ -123,9 +119,7 @@ and its subpackages. For example:
if __name__ == '__main__':
from pyramid.configuration import Configurator
config = Configurator()
- config.begin()
config.scan()
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
diff --git a/docs/narr/contextfinding.rst b/docs/narr/contextfinding.rst
index c3fbe7f5a..770f97d15 100644
--- a/docs/narr/contextfinding.rst
+++ b/docs/narr/contextfinding.rst
@@ -75,7 +75,7 @@ URL dispatch can easily handle URLs such as
``http://example.com/members/Chris``, where it's assumed that each
item "below" ``members`` in the URL represents a single member in some
system. You just match everything "below" ``members`` to a particular
-:term:`view callable`, e.g. ``/members/:memberid``.
+:term:`view callable`, e.g. ``/members/{memberid}``.
However, URL dispatch is not very convenient if you'd like your URLs
to represent an arbitrary hierarchy. For example, if you need to
diff --git a/docs/narr/declarative.rst b/docs/narr/declarative.rst
index 48a3ea134..b9dbcab7d 100644
--- a/docs/narr/declarative.rst
+++ b/docs/narr/declarative.rst
@@ -655,7 +655,7 @@ declaration` causes a route to be added to the application.
<route
name="myroute"
- pattern="/prefix/:one/:two"
+ pattern="/prefix/{one}/{two}"
view=".views.myview"
/>
diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst
index 2aa4064cd..c3fe401ec 100644
--- a/docs/narr/environment.rst
+++ b/docs/narr/environment.rst
@@ -201,6 +201,72 @@ should be changed accordingly.
| |
+-----------------------------+
+Mako Error Handler
+++++++++++++++++++
+
+Python callable which is called whenever Mako compile or runtime exceptions
+occur. The callable is passed the current context as well as the exception. If
+the callable returns True, the exception is considered to be handled, else it
+is re-raised after the function completes. Is used to provide custom
+error-rendering functions.
+
++-----------------------------+
+| Config File Setting Name |
++=============================+
+| ``mako.error_handler`` |
+| |
+| |
+| |
++-----------------------------+
+
+Mako Default Filters
+++++++++++++++++++++
+
+List of string filter names that will be applied to all Mako expressions.
+
++-----------------------------+
+| Config File Setting Name |
++=============================+
+| ``mako.default_filters`` |
+| |
+| |
+| |
++-----------------------------+
+
+Mako Import
++++++++++++
+
+String list of Python statements, typically individual “import” lines, which
+will be placed into the module level preamble of all generated Python modules.
+
+
++-----------------------------+
+| Config File Setting Name |
++=============================+
+| ``mako.imports`` |
+| |
+| |
+| |
++-----------------------------+
+
+
+Mako Strict Undefined
++++++++++++++++++++++
+
+``true`` or ``false``, representing the "strict undefined" behavior of Mako
+(see `Mako Context Variables
+<http://www.makotemplates.org/docs/runtime.html#context-variables>`_). By
+default, this is ``false``.
+
++-----------------------------+
+| Config File Setting Name |
++=============================+
+| ``mako.strict_undefined`` |
+| |
+| |
+| |
++-----------------------------+
+
Examples
--------
diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst
index bc21bf29f..9d3cad13c 100644
--- a/docs/narr/firstapp.rst
+++ b/docs/narr/firstapp.rst
@@ -38,10 +38,8 @@ configured imperatively:
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.add_view(hello_world)
config.add_view(goodbye_world, name='goodbye')
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
@@ -149,10 +147,8 @@ imports and function definitions is placed within the confines of an
if __name__ == '__main__':
config = Configurator()
- config.begin()
config.add_view(hello_world)
config.add_view(goodbye_world, name='goodbye')
- config.end()
app = config.make_wsgi_app()
serve(app, host='0.0.0.0')
@@ -190,29 +186,6 @@ this particular :app:`Pyramid` application. Methods called on the
Configurator will cause registrations to be made in a
:term:`application registry` associated with the application.
-Beginning Configuration
-~~~~~~~~~~~~~~~~~~~~~~~
-
-.. ignore-next-block
-.. code-block:: python
-
- config.begin()
-
-The :meth:`pyramid.configuration.Configurator.begin` method tells
-the system that application configuration has begun. In particular,
-this causes the :term:`application registry` associated with this
-configurator to become the "current" application registry, meaning
-that code which attempts to use the application registry :term:`thread
-local` will obtain the registry associated with the configurator.
-This is an explicit step because it's sometimes convenient to use a
-configurator without causing the registry associated with the
-configurator to become "current".
-
-.. note::
-
- See :ref:`threadlocals_chapter` for a discussion about what it
- means for an application registry to be "current".
-
.. _adding_configuration:
Adding Configuration
@@ -281,28 +254,6 @@ important. We can register ``goodbye_world`` first and
``hello_world`` second; :app:`Pyramid` will still give us the most
specific callable when a request is dispatched to it.
-Ending Configuration
-~~~~~~~~~~~~~~~~~~~~
-
-.. ignore-next-block
-.. code-block:: python
-
- config.end()
-
-The :meth:`pyramid.configuration.Configurator.end` method tells the
-system that application configuration has ended. It is the inverse of
-:meth:`pyramid.configuration.Configurator.begin`. In particular,
-this causes the :term:`application registry` associated with this
-configurator to no longer be the "current" application registry,
-meaning that code which attempts to use the application registry
-:term:`thread local` will no longer obtain the registry associated
-with the configurator.
-
-.. note::
-
- See :ref:`threadlocals_chapter` for a discussion about what it
- means for an application registry to be "current".
-
.. index::
single: make_wsgi_app
single: WSGI application
diff --git a/docs/narr/handlers.rst b/docs/narr/handlers.rst
index b8e7b5d9b..d82f42bdb 100644
--- a/docs/narr/handlers.rst
+++ b/docs/narr/handlers.rst
@@ -59,11 +59,11 @@ be performed in order to register it with the system:
.. code-block:: python
- config.add_handler('hello', '/hello/:action', handler=Hello)
+ config.add_handler('hello', '/hello/{action}', handler=Hello)
This example will result in a route being added for the pattern
-``/hello/:action``, each method of the ``Hello`` class will then be examined
-to register the views. The value of ``:action`` in the route pattern will be
+``/hello/{action}``, each method of the ``Hello`` class will then be examined
+to register the views. The value of ``{action}`` in the route pattern will be
used to determine which view should be called, and each view in the class will
be setup with a view predicate that requires a specific ``action`` name.
@@ -98,7 +98,7 @@ For example:
.. code-block:: python
- config.add_handler('hello', '/hello/:action',
+ config.add_handler('hello', '/hello/{action}',
handler='mypackage.handlers:MyHandler')
In larger applications, it is advised to use a :term:`resource specification`
@@ -168,8 +168,8 @@ information on the handler method which is used by
configuration.
All keyword arguments are recorded, and passed to
-:meth:`!pyramid.configuration.Configurator.add_view`. Any valid keyword
-arguments for :meth:`!pyramid.configuration.Configurator.add_view` can thus be
+:meth:`~pyramid.configuration.Configurator.add_view`. Any valid keyword
+arguments for :meth:`~pyramid.configuration.Configurator.add_view` can thus be
used with the :class:`~pyramid.view.action` decorator to further restrict when
the view will be called.
@@ -219,7 +219,7 @@ Example:
return {}
# in the config
- config.add_handler('hello', '/hello/:action', handler=Hello)
+ config.add_handler('hello', '/hello/{action}', handler=Hello)
With this configuration, the url ``/hello/home`` will find a view configuration
that results in calling the ``show_template`` method, then rendering the
diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst
index 61ac68d5d..e704463c7 100644
--- a/docs/narr/hybrid.rst
+++ b/docs/narr/hybrid.rst
@@ -42,8 +42,8 @@ configuration:
# config is an instance of pyramid.configuration.Configurator
- config.add_route('foobar', ':foo/:bar', view='myproject.views.foobar')
- config.add_route('bazbuz', ':baz/:buz', view='myproject.views.bazbuz')
+ config.add_route('foobar', '{foo}/{bar}', view='myproject.views.foobar')
+ config.add_route('bazbuz', '{baz}/{buz}', view='myproject.views.bazbuz')
Each :term:`route` typically corresponds to a single view callable,
and when that route is matched during a request, the view callable
@@ -185,7 +185,7 @@ of a route's pattern:
.. code-block:: python
:linenos:
- config.add_route('home', ':foo/:bar/*traverse')
+ config.add_route('home', '{foo}/{bar}/*traverse')
A ``*traverse`` token at the end of the pattern in a route's
configuration implies a "remainder" *capture* value. When it is used,
@@ -243,7 +243,7 @@ route configuration statement:
.. code-block:: python
:linenos:
- config.add_route('home', ':foo/:bar/*traverse',
+ config.add_route('home', '{foo}/{bar}/*traverse',
factory='mypackage.routes.root_factory')
The ``factory`` above points at the function we've defined. It
@@ -267,14 +267,14 @@ to do.
When the route configuration named ``home`` above is matched during a
request, the matchdict generated will be based on its pattern:
-``:foo/:bar/*traverse``. The "capture value" implied by the
+``{foo}/{bar}/*traverse``. The "capture value" implied by the
``*traverse`` element in the pattern will be used to traverse the
graph in order to find a context, starting from the root object
returned from the root factory. In the above example, the
:term:`root` object found will be the instance named ``root`` in
``routes.py``.
-If the URL that matched a route with the pattern ``:foo/:bar/*traverse``,
+If the URL that matched a route with the pattern ``{foo}/{bar}/*traverse``,
is ``http://example.com/one/two/a/b/c``, the traversal path used
against the root object will be ``a/b/c``. As a result,
:app:`Pyramid` will attempt to traverse through the edges ``a``,
@@ -296,7 +296,7 @@ invoked after a route matches:
.. code-block:: python
:linenos:
- config.add_route('home', ':foo/:bar/*traverse',
+ config.add_route('home', '{foo}/{bar}/*traverse',
factory='mypackage.routes.root_factory')
config.add_view('mypackage.views.myview', route_name='home')
@@ -326,7 +326,7 @@ when a hybrid route is matched:
.. code-block:: python
:linenos:
- config.add_route('home', ':foo/:bar/*traverse',
+ config.add_route('home', '{foo}/{bar}/*traverse',
factory='mypackage.routes.root_factory')
config.add_view('mypackage.views.myview', name='home')
config.add_view('mypackage.views.another_view', name='another',
@@ -358,7 +358,7 @@ Using the ``traverse`` Argument In a Route Definition
Rather than using the ``*traverse`` remainder marker in a pattern, you
can use the ``traverse`` argument to the
-:meth:`pyramid.configuration.Configurator.add_route`` method.
+:meth:`pyramid.configuration.Configurator.add_route` method.
When you use the ``*traverse`` remainder marker, the traversal path is
limited to being the remainder segments of a request URL when a route
@@ -371,14 +371,14 @@ Here's a use of the ``traverse`` pattern in a call to
.. code-block:: python
:linenos:
- config.add_route('abc', '/articles/:article/edit',
- traverse='/articles/:article')
+ config.add_route('abc', '/articles/{article}/edit',
+ traverse='/articles/{article}')
The syntax of the ``traverse`` argument is the same as it is for
``pattern``.
-If, as above, the ``pattern`` provided is ``articles/:article/edit``,
-and the ``traverse`` argument provided is ``/:article``, when a
+If, as above, the ``pattern`` provided is ``articles/{article}/edit``,
+and the ``traverse`` argument provided is ``/{article}``, when a
request comes in that causes the route to match in such a way that the
``article`` match value is ``1`` (when the request URI is
``/articles/1/edit``), the traversal path will be generated as ``/1``.
@@ -467,7 +467,7 @@ startup time.
.. code-block:: python
:linenos:
- config.add_route('home', ':foo/:bar/*traverse',
+ config.add_route('home', '{foo}/{bar}/*traverse',
view='myproject.views.home')
config.add_view('myproject.views.another', route_name='home')
@@ -479,7 +479,7 @@ supply a view attribute. For example, this ``add_route`` call:
.. code-block:: python
:linenos:
- config.add_route('home', ':foo/:bar/*traverse',
+ config.add_route('home', '{foo}/{bar}/*traverse',
view='myproject.views.home')
Can also be spelled like so:
@@ -487,7 +487,7 @@ Can also be spelled like so:
.. code-block:: python
:linenos:
- config.add_route('home', ':foo/:bar/*traverse')
+ config.add_route('home', '{foo}/{bar}/*traverse')
config.add_view('myproject.views.home', route_name='home')
The two spellings are logically equivalent. In fact, the former is
diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst
index 703883fb2..9e2071872 100644
--- a/docs/narr/i18n.rst
+++ b/docs/narr/i18n.rst
@@ -773,8 +773,8 @@ If this setting is supplied within the :app:`Pyramid` application
.. code-block:: python
:linenos:
- from pyramid.setttings import get_settings
- settings = get_settings()
+ from pyramid.threadlocal import get_current_registry
+ settings = get_current_registry().settings
default_locale_name = settings['default_locale_name']
"Detecting" Available Languages
@@ -822,8 +822,9 @@ Then as a part of the code of a custom :term:`locale negotiator`:
.. code-block:: py
- from pyramid.settings import get_settings
- languages = get_settings()['available_languages'].split()
+ from pyramid.threadlocal import get_current_registry
+ settings = get_current_registry().settings
+ languages = settings['available_languages'].split()
This is only a suggestion. You can create your own "available
languages" configuration scheme as necessary.
diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst
index 725d32725..7c725690d 100644
--- a/docs/narr/introduction.rst
+++ b/docs/narr/introduction.rst
@@ -103,7 +103,7 @@ What Is The Pylons Project?
---------------------------
:app:`Pyramid` is a member of the collection of software published under the
-Pylons Project. :Pylons software is written by a loose-knit community of
+Pylons Project. Pylons software is written by a loose-knit community of
contributors. The `Pylons Project website <http://docs.pylonshq.com>`_
includes details about how :app:`Pyramid` relates to the Pylons Project.
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index f47e9f293..aef134ff7 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -316,12 +316,13 @@ Python interpreter shell unconditionally.
[pipeline:main]
pipeline = egg:WebError#evalerror
- myapp
+ MyProject
- If you use ``main`` as the section name argument instead of ``myapp``
- against the above ``.ini`` file, an error will occur. Use the most
- specific reference to your application within the ``.ini`` file possible
- as the section name argument.
+ Use ``MyProject`` instead of ``main`` as the section name argument to
+ ``pshell`` against the above ``.ini`` file (e.g. ``paster pshell
+ development.ini MyProject``). If you use ``main`` instead, an error will
+ occur. Use the most specific reference to your application within the
+ ``.ini`` file possible as the section name argument.
Press ``Ctrl-D`` to exit the interactive shell (or ``Ctrl-Z`` on Windows).
@@ -740,14 +741,14 @@ also informs Python that the directory which contains it is a *package*.
#. Line 2 imports the ``get_root`` function from
:mod:`myproject.models` that we use later.
-#. Lines 4-14 define a function that returns a :app:`Pyramid`
+#. Lines 4-12 define a function that returns a :app:`Pyramid`
WSGI application. This function is meant to be called
by the :term:`PasteDeploy` framework as a result of running
``paster serve``.
Within this function, configuration is performed.
- Lines 9-11 register a "default view" (a view that has no ``name``
+ Lines 8-10 register a "default view" (a view that has no ``name``
attribute). It is registered so that it will be found when the
:term:`context` of the request is an instance of the
:class:`myproject.models.MyModel` class. The first argument to
@@ -761,11 +762,11 @@ also informs Python that the directory which contains it is a *package*.
``templates`` directory of the ``myproject`` package. The template file
it actually points to is a :term:`Chameleon` ZPT template file.
- Line 12 registers a static view, which will serve up the files from the
+ Line 11 registers a static view, which will serve up the files from the
``mypackage:static`` :term:`resource specification` (the ``static``
directory of the ``mypackage`` package).
- Line 14 returns a :term:`WSGI` application to the caller of the function
+ Line 12 returns a :term:`WSGI` application to the caller of the function
(Paste).
``views.py``
diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst
index c57525f4c..427acc319 100644
--- a/docs/narr/startup.rst
+++ b/docs/narr/startup.rst
@@ -86,9 +86,9 @@ press ``return`` after running ``paster serve development.ini``.
:linenos:
In this case, the ``myproject.run:app`` function referred to by the entry
- point URI ``egg:MyProject#app`` (see :ref:`MyProject_ini` for more
- information about entry point URIs, and how they relate to callables),
- will receive the key/value pairs ``{'reload_templates':'true',
+ point URI ``egg:MyProject`` (see :ref:`MyProject_ini` for more information
+ about entry point URIs, and how they relate to callables), will receive
+ the key/value pairs ``{'reload_templates':'true',
'debug_authorization':'false', 'debug_notfound':'false',
'debug_templates':'true', 'default_locale_name':'en'}``.
diff --git a/docs/narr/static.rst b/docs/narr/static.rst
index efeabd012..a01cbbabf 100644
--- a/docs/narr/static.rst
+++ b/docs/narr/static.rst
@@ -69,22 +69,21 @@ when generating a URL using :func:`pyramid.url.static_url`.
.. note::
Using :func:`pyramid.url.static_url` in conjunction with a
- :meth:`pyramid.configuration.Configurator.add_static_view` makes
- it possible to put static media on a separate webserver during
- production (if the ``name`` argument to
- :meth:`pyramid.configuration.Configurator.add_static_view` is a
- URL), while keeping static media package-internal and served by the
- development webserver during development (if the ``name`` argument
- to :meth:`pyramid.configuration.Configurator.add_static_view` is
- a view name). To create such a circumstance, we suggest using the
- :func:`pyramid.settings.get_settings` API in conjunction with a
- setting in the application ``.ini`` file named ``media_location``.
- Then set the value of ``media_location`` to either a view name or a
- URL depending on whether the application is being run in
- development or in production (use a different `.ini`` file for
- production than you do for development). This is just a suggestion
- for a pattern; any setting name other than ``media_location`` could
- be used.
+ :meth:`pyramid.configuration.Configurator.add_static_view` makes it
+ possible to put static media on a separate webserver during production (if
+ the ``name`` argument to
+ :meth:`pyramid.configuration.Configurator.add_static_view` is a URL),
+ while keeping static media package-internal and served by the development
+ webserver during development (if the ``name`` argument to
+ :meth:`pyramid.configuration.Configurator.add_static_view` is a view
+ name). To create such a circumstance, we suggest using the
+ :attr:`pyramid.registry.Registry.settings` API in conjunction with a
+ setting in the application ``.ini`` file named ``media_location``. Then
+ set the value of ``media_location`` to either a view name or a URL
+ depending on whether the application is being run in development or in
+ production (use a different `.ini`` file for production than you do for
+ development). This is just a suggestion for a pattern; any setting name
+ other than ``media_location`` could be used.
For example, :meth:`pyramid.configuration.Configurator.add_static_view` may
be fed a ``name`` argument which is ``http://example.com/images``:
diff --git a/docs/narr/unittesting.rst b/docs/narr/unittesting.rst
index 5cd8a0683..26035726b 100644
--- a/docs/narr/unittesting.rst
+++ b/docs/narr/unittesting.rst
@@ -1,4 +1,4 @@
-.. index::
+\.. index::
single: unit testing
single: integration testing
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 4442be355..df7d592f9 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -92,7 +92,12 @@ registry`. Here's an example:
# pyramid.configuration.Configurator class; "myview" is assumed
# to be a "view callable" function
from views import myview
- config.add_route('myroute', '/prefix/:one/:two', view=myview)
+ config.add_route('myroute', '/prefix/{one}/{two}', view=myview)
+
+.. versionchanged:: 1.0a4
+ Prior to 1.0a4, routes allow for a marker starting with a ``:``, for
+ example ``/prefix/{one}``. Starting in 1.0a4, this style is deprecated
+ in favor or ``{}`` usage which allows for additional functionality.
.. index::
single: route configuration; view callable
@@ -116,7 +121,7 @@ Here's an example route configuration that references a view callable:
# pyramid.configuration.Configurator class; "myview" is assumed
# to be a "view callable" function
from myproject.views import myview
- config.add_route('myroute', '/prefix/:one/:two', view=myview)
+ config.add_route('myroute', '/prefix/{one}/{two}', view=myview)
You can also pass a :term:`dotted Python name` as the ``view`` argument
rather than an actual callable:
@@ -128,7 +133,7 @@ rather than an actual callable:
# pyramid.configuration.Configurator class; "myview" is assumed
# to be a "view callable" function
from myproject.views import myview
- config.add_route('myroute', '/prefix/:one/:two',
+ config.add_route('myroute', '/prefix/{one}/{two}',
view='myproject.views.myview')
When a route configuration names a ``view`` attribute, the :term:`view
@@ -200,28 +205,28 @@ the following patterns are equivalent:
.. code-block:: text
- :foo/bar/baz
+ {foo}/bar/baz
and:
.. code-block:: text
- /:foo/bar/baz
+ /{foo}/bar/baz
-A patttern segment (an individual item between ``/`` characters in the
-pattern) may either be a literal string (e.g. ``foo``) *or* it may be
-a segment replacement marker (e.g. ``:foo``) or a certain combination
-of both.
+A pattern segment (an individual item between ``/`` characters in the pattern)
+may either be a literal string (e.g. ``foo``) *or* it may be a replacement
+marker (e.g. ``{foo}``) or a certain combination of both. A replacement marker
+does not need to be preceded by a ``/`` character.
-A segment replacement marker is in the format ``:name``, where this
-means "accept any characters up to the next nonalphaunumeric character
+A replacement marker is in the format ``{name}``, where this
+means "accept any characters up to the next non-alphanumeric character
and use this as the ``name`` matchdict value." For example, the
following pattern defines one literal segment ("foo") and two dynamic
-segments ("baz", and "bar"):
+replacement markers ("baz", and "bar"):
.. code-block:: text
- foo/:baz/:bar
+ foo/{baz}/{bar}
The above pattern will match these URLs, generating the following
matchdicts:
@@ -244,24 +249,36 @@ pattern. So, for instance, if this route pattern was used:
.. code-block:: text
- foo/:name.html
+ foo/{name}.html
The literal path ``/foo/biz.html`` will match the above route pattern,
and the match result will be ``{'name':u'biz'}``. However, the
literal path ``/foo/biz`` will not match, because it does not contain
a literal ``.html`` at the end of the segment represented by
-``:name.html`` (it only contains ``biz``, not ``biz.html``).
+``{name}.html`` (it only contains ``biz``, not ``biz.html``).
+
+To capture both segments, two replacement markers can be used:
+
+.. code-block:: text
+
+ foo/{name}.{ext}
-This does not mean, however, that you can use two segment replacement
-markers in the same segment. For instance, ``/:foo:bar`` is a
-nonsensical route pattern. It will never match anything.
+The literal path ``/foo/biz.html`` will match the above route pattern, and the
+match result will be ``{'name': 'biz', 'ext': 'html'}``. This occurs because
+the replacement marker ``{name}`` has a literal part of ``.`` between the other
+replacement marker ``:ext``.
+
+It is possible to use two replacement markers without any literal characters
+between them, for instance ``/{foo}{bar}``. This would be a nonsensical pattern
+without specifying a custom regular expression to restrict what a marker
+captures.
Segments must contain at least one character in order to match a
segment replacement marker. For example, for the URL ``/abc/``:
-- ``/abc/:foo`` will not match.
+- ``/abc/{foo}`` will not match.
-- ``/:foo/`` will match.
+- ``/{foo}/`` will match.
Note that values representing path segments matched with a
``:segment`` match will be url-unquoted and decoded from UTF-8 into
@@ -270,7 +287,7 @@ pattern:
.. code-block:: text
- foo/:bar
+ foo/{bar}
When matching the following URL:
@@ -292,7 +309,7 @@ need to be preceded by a slash. For example:
.. code-block:: text
- foo/:baz/:bar*fizzle
+ foo/{baz}/{bar}*fizzle
The above pattern will match these URLs, generating the following
matchdicts:
@@ -324,6 +341,25 @@ Will generate the following matchdict:
{'fizzle':(u'La Pe\xf1a', u'a', u'b', u'c')}
+By default, the ``*stararg`` will parse the remainder sections into a tuple
+split by segment. Changing the regular expression used to match a marker can
+also capture the remainder of the URL, for example:
+
+.. code-block:: text
+
+ foo/{baz}/{bar}{fizzle:.*}
+
+The above pattern will match these URLs, generating the following
+matchdicts::
+
+ foo/1/2/ -> {'baz':'1', 'bar':'2', 'fizzle':()}
+ foo/abc/def/a/b/c -> {'baz':'abc', 'bar':'def', 'fizzle': 'a/b/c')}
+
+This occurs because the default regular expression for a marker is ``[^/]+``
+which will match everything up to the first ``/``, while ``{filzzle:.*}`` will
+result in a regular expression match of ``.*`` capturing the remainder into
+a single value.
+
.. index::
single: route ordering
@@ -348,12 +384,12 @@ be added in the following order:
.. code-block:: text
- members/:def
+ members/{def}
members/abc
In such a configuration, the ``members/abc`` pattern would *never* be
matched; this is because the match ordering will always match
-``members/:def`` first; the route configuration with ``members/abc``
+``members/{def}`` first; the route configuration with ``members/abc``
will never be evaluated.
.. index::
@@ -434,8 +470,8 @@ represent neither predicates nor view configuration information.
The syntax of the ``traverse`` argument is the same as it is for
``pattern``. For example, if the ``pattern`` provided is
- ``articles/:article/edit``, and the ``traverse`` argument provided
- is ``/:article``, when a request comes in that causes the route to
+ ``articles/{article}/edit``, and the ``traverse`` argument provided
+ is ``/{article}``, when a request comes in that causes the route to
match in such a way that the ``article`` match value is '1' (when
the request URI is ``/articles/1/edit``), the traversal path will be
generated as ``/1``. This means that the root object's
@@ -462,7 +498,7 @@ represent neither predicates nor view configuration information.
**Predicate Arguments**
``pattern``
- The path of the route e.g. ``ideas/:idea``. This argument is
+ The path of the route e.g. ``ideas/{idea}``. This argument is
required. See :ref:`route_path_pattern_syntax` for information
about the syntax of route paths. If the path doesn't match the
current URL, route matching continues.
@@ -470,7 +506,7 @@ represent neither predicates nor view configuration information.
.. note:: In earlier releases of this framework, this argument existed
as ``path``. ``path`` continues to work as an alias for
``pattern``.
-
+
``xhr``
This value should be either ``True`` or ``False``. If this value is
specified and is ``True``, the :term:`request` must possess an
@@ -644,7 +680,7 @@ match. For example:
num_one_two_or_three = any_of('num', 'one', 'two', 'three')
- config.add_route('num', '/:num',
+ config.add_route('num', '/{num}',
custom_predicates=(num_one_two_or_three,))
The above ``any_of`` function generates a predicate which ensures that
@@ -675,7 +711,7 @@ For instance, a predicate might do some type conversion of values:
ymd_to_int = integers('year', 'month', 'day')
- config.add_route('num', '/:year/:month/:day',
+ config.add_route('num', '/{year}/{month}/{day}',
custom_predicates=(ymd_to_int,))
Note that a conversion predicate is still a predicate so it must
@@ -683,6 +719,29 @@ return ``True`` or ``False``; a predicate that does *only* conversion,
such as the one we demonstrate above should unconditionally return
``True``.
+To avoid the try/except uncertainty, the route pattern can contain regular
+expressions specifying requirements for that marker. For instance:
+
+.. code-block:: python
+ :linenos:
+
+ def integers(*segment_names):
+ def predicate(info, request):
+ match = info['match']
+ for segment_name in segment_names:
+ match[segment_name] = int(match[segment_name])
+ return True
+ return predicate
+
+ ymd_to_int = integers('year', 'month', 'day')
+
+ config.add_route('num', '/{year:\d+}/{month:\d+}/{day:\d+}',
+ custom_predicates=(ymd_to_int,))
+
+Now the try/except is no longer needed because the route will not match at
+all unless these markers match ``\d+`` which requires them to be valid digits
+for an ``int`` type conversion.
+
The ``match`` dictionary passed within ``info`` to each predicate
attached to a route will be the same dictionary. Therefore, when
registering a custom predicate which modifies the ``match`` dict, the
@@ -713,9 +772,9 @@ An example of using the route in a set of route predicates:
if info['route'].name in ('ymd', 'ym', 'y'):
return info['match']['year'] == '2010'
- config.add_route('y', '/:year', custom_predicates=(twenty_ten,))
- config.add_route('ym', '/:year/:month', custom_predicates=(twenty_ten,))
- config.add_route('ymd', '/:year/:month:/day',
+ config.add_route('y', '/{year}', custom_predicates=(twenty_ten,))
+ config.add_route('ym', '/{year}/{month}', custom_predicates=(twenty_ten,))
+ config.add_route('ymd', '/{year}/{month}/{day}',
custom_predicates=(twenty_ten,))
The above predicate, when added to a number of route configurations
@@ -814,7 +873,7 @@ The simplest route declaration which configures a route match to
.. code-block:: python
:linenos:
- config.add_route('idea', 'site/:id', view='mypackage.views.site_view')
+ config.add_route('idea', 'site/{id}', view='mypackage.views.site_view')
When a route configuration with a ``view`` attribute is added to the
system, and an incoming request matches the *pattern* of the route
@@ -822,12 +881,12 @@ configuration, the :term:`view callable` named as the ``view``
attribute of the route configuration will be invoked.
In the case of the above example, when the URL of a request matches
-``/site/:id``, the view callable at the Python dotted path name
+``/site/{id}``, the view callable at the Python dotted path name
``mypackage.views.site_view`` will be called with the request. In
other words, we've associated a view callable directly with a route
pattern.
-When the ``/site/:id`` route pattern matches during a request, the
+When the ``/site/{id}`` route pattern matches during a request, the
``site_view`` view callable is invoked with that request as its sole
argument. When this route matches, a ``matchdict`` will be generated
and attached to the request as ``request.matchdict``. If the specific
@@ -860,30 +919,30 @@ might add to your application:
.. code-block:: python
:linenos:
- config.add_route('idea', 'ideas/:idea', view='mypackage.views.idea_view')
- config.add_route('user', 'users/:user', view='mypackage.views.user_view')
- config.add_route('tag', 'tags/:tags', view='mypackage.views.tag_view')
+ config.add_route('idea', 'ideas/{idea}', view='mypackage.views.idea_view')
+ config.add_route('user', 'users/{user}', view='mypackage.views.user_view')
+ config.add_route('tag', 'tags/{tags}', view='mypackage.views.tag_view')
The above configuration will allow :app:`Pyramid` to service URLs
in these forms:
.. code-block:: text
- /ideas/:idea
- /users/:user
- /tags/:tag
+ /ideas/{idea}
+ /users/{user}
+ /tags/{tag}
-- When a URL matches the pattern ``/ideas/:idea``, the view callable
+- When a URL matches the pattern ``/ideas/{idea}``, the view callable
available at the dotted Python pathname ``mypackage.views.idea_view`` will
be called. For the specific URL ``/ideas/1``, the ``matchdict`` generated
and attached to the :term:`request` will consist of ``{'idea':'1'}``.
-- When a URL matches the pattern ``/users/:user``, the view callable
+- When a URL matches the pattern ``/users/{user}``, the view callable
available at the dotted Python pathname ``mypackage.views.user_view`` will
be called. For the specific URL ``/users/1``, the ``matchdict`` generated
and attached to the :term:`request` will consist of ``{'user':'1'}``.
-- When a URL matches the pattern ``/tags/:tag``, the view callable available
+- When a URL matches the pattern ``/tags/{tag}``, the view callable available
at the dotted Python pathname ``mypackage.views.tag_view`` will be called.
For the specific URL ``/tags/1``, the ``matchdict`` generated and attached
to the :term:`request` will consist of ``{'tag':'1'}``.
@@ -911,7 +970,7 @@ An example of using a route with a factory:
.. code-block:: python
:linenos:
- config.add_route('idea', 'ideas/:idea',
+ config.add_route('idea', 'ideas/{idea}',
view='myproject.views.idea_view',
factory='myproject.models.Idea')
@@ -939,7 +998,7 @@ a ``view`` declaration.
.. code-block:: python
:linenos:
- config.add_route('idea', 'site/:id')
+ config.add_route('idea', 'site/{id}')
config.add_view(route_name='idea', view='mypackage.views.site_view')
This set of configuration parameters creates a configuration
@@ -949,7 +1008,7 @@ completely equivalent to this example provided in
.. code-block:: python
:linenos:
- config.add_route('idea', 'site/:id', view='mypackage.views.site_view')
+ config.add_route('idea', 'site/{id}', view='mypackage.views.site_view')
In fact, the spelling which names a ``view`` attribute is just
syntactic sugar for the more verbose spelling which contains separate
@@ -990,7 +1049,7 @@ Generating Route URLs
Use the :func:`pyramid.url.route_url` function to generate URLs based on
route patterns. For example, if you've configured a route with the ``name``
-"foo" and the ``pattern`` ":a/:b/:c", you might do this.
+"foo" and the ``pattern`` "{a}/{b}/{c}", you might do this.
.. ignore-next-block
.. code-block:: python
@@ -1184,7 +1243,7 @@ Such a ``factory`` might look like so:
if article == '1':
self.__acl__ = [ (Allow, 'editor', 'view') ]
-If the route ``archives/:article`` is matched, and the article number
+If the route ``archives/{article}`` is matched, and the article number
is ``1``, :app:`Pyramid` will generate an ``Article``
:term:`context` with an ACL on it that allows the ``editor`` principal
the ``view`` permission. Obviously you can do more generic things
diff --git a/docs/tutorials/gae/index.rst b/docs/tutorials/gae/index.rst
index a2b190a31..9c8e8c07e 100644
--- a/docs/tutorials/gae/index.rst
+++ b/docs/tutorials/gae/index.rst
@@ -72,13 +72,13 @@ system.
#. Edit ``config.py``
Edit the ``APP_NAME`` and ``APP_ARGS`` settings within
- ``config.py``. The ``APP_NAME`` must be ``pyramidapp:app``, and
+ ``config.py``. The ``APP_NAME`` must be ``pyramidapp:main``, and
the APP_ARGS must be ``({},)``. Any other settings in
``config.py`` should remain the same.
.. code-block:: python
- APP_NAME = 'pyramidapp:app'
+ APP_NAME = 'pyramidapp:main'
APP_ARGS = ({},)
#. Edit ``runner.py``
diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst
index c05a53831..a94a1632d 100644
--- a/docs/tutorials/wiki/basiclayout.rst
+++ b/docs/tutorials/wiki/basiclayout.rst
@@ -48,14 +48,11 @@ entry point happens to be the ``app`` function within the file named
factory` and the settings keywords parsed by PasteDeploy. The root
factory is named ``get_root``.
-#. *Lines 16-18*. Begin configuration using the ``begin`` method of
- the :meth:`pyramid.configuration.Configurator` class, load the
+#. *Line 16*. Load the
``configure.zcml`` file from our package using the
- :meth:`pyramid.configuration.Configurator.load_zcml` method, and
- end configuration using the
- :meth:`pyramid.configuration.Configurator.end` method.
+ :meth:`pyramid.configuration.Configurator.load_zcml` method.
-#. *Line 19*. Use the
+#. *Line 17*. Use the
:meth:`pyramid.configuration.Configurator.make_wsgi_app` method
to return a :term:`WSGI` application.
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py
index 124647ba9..a8a3c513e 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py
@@ -16,7 +16,5 @@ def main(global_config, **settings):
def get_root(request):
return finder(request.environ)
config = Configurator(root_factory=get_root, settings=settings)
- config.begin()
config.load_zcml('configure.zcml')
- config.end()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py
index f6cf8b479..45e4d722b 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py
@@ -13,8 +13,6 @@ def main(global_config, **settings):
def get_root(request):
return finder(request.environ)
config = Configurator(root_factory=get_root, settings=settings)
- config.begin()
config.load_zcml('configure.zcml')
- config.end()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki/src/models/tutorial/__init__.py b/docs/tutorials/wiki/src/models/tutorial/__init__.py
index 7ef07e767..66e2f05ee 100644
--- a/docs/tutorials/wiki/src/models/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/models/tutorial/__init__.py
@@ -16,8 +16,6 @@ def main(global_config, **settings):
def get_root(request):
return finder(request.environ)
config = Configurator(root_factory=get_root, settings=settings)
- config.begin()
config.load_zcml('configure.zcml')
- config.end()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py b/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py
index 7ef07e767..66e2f05ee 100644
--- a/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py
@@ -16,8 +16,6 @@ def main(global_config, **settings):
def get_root(request):
return finder(request.environ)
config = Configurator(root_factory=get_root, settings=settings)
- config.begin()
config.load_zcml('configure.zcml')
- config.end()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki/src/views/tutorial/__init__.py b/docs/tutorials/wiki/src/views/tutorial/__init__.py
index 7ef07e767..66e2f05ee 100644
--- a/docs/tutorials/wiki/src/views/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/views/tutorial/__init__.py
@@ -16,8 +16,6 @@ def main(global_config, **settings):
def get_root(request):
return finder(request.environ)
config = Configurator(root_factory=get_root, settings=settings)
- config.begin()
config.load_zcml('configure.zcml')
- config.end()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst
index 9aca94fe5..01fdd351e 100644
--- a/docs/tutorials/wiki2/basiclayout.rst
+++ b/docs/tutorials/wiki2/basiclayout.rst
@@ -32,30 +32,21 @@ entry point happens to be the ``app`` function within the file named
#. *Lines 1-4*. Imports to support later code.
-#. *Lines 9-11*. Get the database configuration string from the
- ``development.ini`` file's ``[app:sqlalchemy]`` section. This will be a
- URI (something like ``sqlite://``).
+#. *Line 9*. Create a SQLAlchemy database engine from the ``sqlalchemy.``
+ prefixed settings in the ``development.ini`` file's ``[app:tutorial]``
+ section. This will be a URI (something like ``sqlite://``).
-#. *Line 12*. Get the database echo setting from ``development.ini``
- file's ``[app:sqlalchemy]`` section. This will either be ``true``
- or ``false``. If ``true``, the application will print SQL to the
- console as it is generated and run by SQLAlchemy. By default, it
- is false.
+#. *Line 10*. We initialize our SQL database using SQLAlchemy, passing
+ it the engine
-#. Line *13*. We initialize our SQL database using SQLAlchemy, passing
- it the db string and a variant of the db_echo value.
-
-#. *Line 14*. We construct a :term:`Configurator`. ``settings`` is
+#. *Line 11*. We construct a :term:`Configurator`. ``settings`` is
passed as a keyword argument with the dictionary values passed by
PasteDeploy as the ``settings`` argument. This will be a
dictionary of settings parsed by PasteDeploy, which contains
deployment-related values such as ``reload_templates``,
``db_string``, etc.
-#. *Line 15*. We call :meth:`pyramid.configuration.Configurator.begin` which
- tells the configuration machinery we are starting configuration.
-
-#. *Line 16*. We call
+#. *Line 12*. We call
:meth:`pyramid.configuration.Configurator.add_static_view` with the
arguments ``static`` (the name), and ``tutorial:static`` (the path). This
registers a static resource view which will match any URL that starts with
@@ -67,7 +58,7 @@ entry point happens to be the ``app`` function within the file named
``/static/foo``) will be used to compose a path to a static file resource,
such as a CSS file.
-#. *Lines 17-18*. Register a :term:`route configuration` via the
+#. *Lines 13-14*. Register a :term:`route configuration` via the
:meth:`pyramid.configuration.Configurator.add_route` method that will be
used when the URL is ``/``. Since this route has an ``pattern`` equalling
``/`` it is the "default" route. The argument named ``view`` with the
@@ -81,10 +72,7 @@ entry point happens to be the ``app`` function within the file named
``tutorial.views.my_view`` view returns a dictionary, a :term:`renderer`
will use this template to create a response.
-#. *Line 19*. We call :meth:`pyramid.configuration.Configurator.end` which
- tells the configuration machinery we are ending configuration.
-
-#. *Line 20*. We use the
+#. *Line 15*. We use the
:meth:`pyramid.configuration.Configurator.make_wsgi_app` method to return
a :term:`WSGI` application.
@@ -103,29 +91,28 @@ Here is the source for ``models.py``:
:linenos:
:language: py
-#. *Lines 1-14*. Imports to support later code.
+#. *Lines 1-13*. Imports to support later code.
-#. *Line 16*. We set up a SQLAlchemy "DBSession" object here. We
+#. *Line 15*. We set up a SQLAlchemy "DBSession" object here. We
specify that we'd like to use the "ZopeTransactionExtension". This
extension is an extension which allows us to use a *transaction
manager* instead of controlling commits and aborts to database
operations by hand.
-#. *Line 17*. We create a declarative ``Base`` object to use as a
+#. *Line 16*. We create a declarative ``Base`` object to use as a
base class for our model.
-#. *Lines 19-27*. A model class named ``MyModel``. It has an
+#. *Lines 18-26*. A model class named ``MyModel``. It has an
``__init__`` that takes a two arguments (``name``, and ``value``).
It stores these values as ``self.name`` and ``self.value`` within
the ``__init__`` function itself. The ``MyModel`` class also has a
``__tablename__`` attribute. This informs SQLAlchemy which table
to use to store the data representing instances of this class.
-#. *Lines 29-34*. A function named ``populate`` which adds a single
+#. *Lines 28-33*. A function named ``populate`` which adds a single
model instance into our SQL storage and commits a transaction.
-#. *Lines 36-44*. A function named ``initialize_sql`` which sets up
- an actual SQL database and binds it to our SQLAlchemy DBSession
- object. It also calls the ``populate`` function, to do initial
- database population.
+#. *Lines 35-42*. A function named ``initialize_sql`` which receives a SQL
+ database engine and binds it to our SQLAlchemy DBSession object. It also
+ calls the ``populate`` function, to do initial database population.
diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst
index b87cd6a64..0f446bb4e 100644
--- a/docs/tutorials/wiki2/definingviews.rst
+++ b/docs/tutorials/wiki2/definingviews.rst
@@ -24,7 +24,7 @@ The request passed to every view that is called as the result of a route
match has an attribute named ``matchdict`` that contains the elements placed
into the URL by the ``pattern`` of a ``route`` statement. For instance, if a
call to :meth:`pyramid.configuration.Configurator.add_route` in
-``__init__.py`` had the pattern ``:one/:two``, and the URL at
+``__init__.py`` had the pattern ``{one}/{two}``, and the URL at
``http://example.com/foo/bar`` was invoked, matching this pattern, the
matchdict dictionary attached to the request passed to the view would have a
``one`` key with the value ``foo`` and a ``two`` key with the value ``bar``.
@@ -277,16 +277,16 @@ the order they're found in the ``__init__.py`` file.
to the view named ``view_wiki`` in our ``views.py`` file with the name
``view_wiki``. This is the :term:`default view` for the wiki.
-#. Add a declaration which maps the pattern ``/:pagename`` to the view named
+#. Add a declaration which maps the pattern ``/{pagename}`` to the view named
``view_page`` in our ``views.py`` file with the view name ``view_page``.
This is the regular view for a page.
#. Add a declaration which maps the pattern
- ``/add_page/:pagename`` to the view named ``add_page`` in our
+ ``/add_page/{pagename}`` to the view named ``add_page`` in our
``views.py`` file with the name ``add_page``. This is the add view
for a new page.
-#. Add a declaration which maps the pattern ``/:pagename/edit_page`` to the
+#. Add a declaration which maps the pattern ``/{pagename}/edit_page`` to the
view named ``edit_page`` in our ``views.py`` file with the name
``edit_page``. This is the edit view for a page.
diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini
index e1d0ab598..23b01a338 100644
--- a/docs/tutorials/wiki2/src/authorization/development.ini
+++ b/docs/tutorials/wiki2/src/authorization/development.ini
@@ -5,8 +5,7 @@ debug_authorization = false
debug_notfound = false
debug_templates = true
default_locale_name = en
-db_string = sqlite:///%(here)s/tutorial.db
-db_echo = false
+sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[pipeline:main]
pipeline =
@@ -18,3 +17,37 @@ pipeline =
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, sqlalchemy
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
index 4bddea74c..dbac349b9 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
@@ -2,7 +2,7 @@ from pyramid.configuration import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
-from pyramid.settings import asbool
+from sqlalchemy import engine_from_config
from tutorial.models import initialize_sql
from tutorial.security import groupfinder
@@ -10,11 +10,8 @@ from tutorial.security import groupfinder
def main(global_config, **settings):
""" This function returns a WSGI application.
"""
- db_string = settings.get('db_string')
- if db_string is None:
- raise ValueError("No 'db_string' value in application configuration.")
- db_echo = settings.get('db_echo', 'false')
- initialize_sql(db_string, asbool(db_echo))
+ engine = engine_from_config(settings, 'sqlalchemy.')
+ initialize_sql(engine)
authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
@@ -22,7 +19,6 @@ def main(global_config, **settings):
root_factory='tutorial.models.RootFactory',
authentication_policy=authn_policy,
authorization_policy=authz_policy)
- config.begin()
config.add_static_view('static', 'tutorial:static')
config.add_route('view_wiki', '/', view='tutorial.views.view_wiki')
config.add_route('login', '/login',
@@ -30,20 +26,19 @@ def main(global_config, **settings):
view_renderer='tutorial:templates/login.pt')
config.add_route('logout', '/logout',
view='tutorial.login.logout')
- config.add_route('view_page', '/:pagename',
+ config.add_route('view_page', '/{pagename}',
view='tutorial.views.view_page',
view_renderer='tutorial:templates/view.pt')
- config.add_route('add_page', '/add_page/:pagename',
+ config.add_route('add_page', '/add_page/{pagename}',
view='tutorial.views.add_page',
view_renderer='tutorial:templates/edit.pt',
view_permission='edit')
- config.add_route('edit_page', '/:pagename/edit_page',
+ config.add_route('edit_page', '/{pagename}/edit_page',
view='tutorial.views.edit_page',
view_renderer='tutorial:templates/edit.pt',
view_permission='edit')
config.add_view('tutorial.login.login',
renderer='tutorial:templates/login.pt',
context='pyramid.exceptions.Forbidden')
- config.end()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models.py b/docs/tutorials/wiki2/src/authorization/tutorial/models.py
index 7580220b6..487299c4c 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models.py
@@ -3,7 +3,6 @@ import transaction
from pyramid.security import Allow
from pyramid.security import Everyone
-from sqlalchemy import create_engine
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Text
@@ -30,8 +29,7 @@ class Page(Base):
self.name = name
self.data = data
-def initialize_sql(db_string, echo=False):
- engine = create_engine(db_string, echo=echo)
+def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py
index 65330ce17..c78899797 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py
@@ -14,9 +14,9 @@ def _initTestingDB():
return DBSession
def _registerRoutes(config):
- config.add_route('view_page', ':pagename')
- config.add_route('edit_page', ':pagename/edit_page')
- config.add_route('add_page', 'add_page/:pagename')
+ config.add_route('view_page', '{pagename}')
+ config.add_route('edit_page', '{pagename}/edit_page')
+ config.add_route('add_page', 'add_page/{pagename}')
class ViewWikiTests(unittest.TestCase):
def setUp(self):
@@ -28,7 +28,7 @@ class ViewWikiTests(unittest.TestCase):
def test_it(self):
from tutorial.views import view_wiki
- self.config.add_route('view_page', ':pagename')
+ self.config.add_route('view_page', '{pagename}')
request = testing.DummyRequest()
response = view_wiki(request)
self.assertEqual(response.location, 'http://example.com/FrontPage')
diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini
index e1d0ab598..23b01a338 100644
--- a/docs/tutorials/wiki2/src/basiclayout/development.ini
+++ b/docs/tutorials/wiki2/src/basiclayout/development.ini
@@ -5,8 +5,7 @@ debug_authorization = false
debug_notfound = false
debug_templates = true
default_locale_name = en
-db_string = sqlite:///%(here)s/tutorial.db
-db_echo = false
+sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[pipeline:main]
pipeline =
@@ -18,3 +17,37 @@ pipeline =
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, sqlalchemy
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
index 7a701fc02..5236a538d 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
@@ -1,22 +1,17 @@
from pyramid.configuration import Configurator
-from pyramid.settings import asbool
+from sqlalchemy import engine_from_config
from tutorial.models import initialize_sql
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
- db_string = settings.get('db_string')
- if db_string is None:
- raise ValueError("No 'db_string' value in application configuration.")
- db_echo = settings.get('db_echo', 'false')
- initialize_sql(db_string, asbool(db_echo))
+ engine = engine_from_config(settings, 'sqlalchemy.')
+ initialize_sql(engine)
config = Configurator(settings=settings)
- config.begin()
config.add_static_view('static', 'tutorial:static')
config.add_route('home', '/', view='tutorial.views.my_view',
view_renderer='templates/mytemplate.pt')
- config.end()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
index a1726ebf4..9da906752 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
@@ -1,6 +1,5 @@
import transaction
-from sqlalchemy import create_engine
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Unicode
@@ -33,8 +32,7 @@ def populate():
session.flush()
transaction.commit()
-def initialize_sql(db_string, db_echo=False):
- engine = create_engine(db_string, echo=db_echo)
+def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py
index 72f0c89d8..2db1bc5b6 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py
@@ -3,8 +3,9 @@ from pyramid.configuration import Configurator
from pyramid import testing
def _initTestingDB():
+ from sqlalchemy import create_engine
from tutorial.models import initialize_sql
- session = initialize_sql('sqlite://')
+ session = initialize_sql(create_engine('sqlite://'))
return session
class TestMyView(unittest.TestCase):
diff --git a/docs/tutorials/wiki2/src/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini
index e1d0ab598..23b01a338 100644
--- a/docs/tutorials/wiki2/src/models/development.ini
+++ b/docs/tutorials/wiki2/src/models/development.ini
@@ -5,8 +5,7 @@ debug_authorization = false
debug_notfound = false
debug_templates = true
default_locale_name = en
-db_string = sqlite:///%(here)s/tutorial.db
-db_echo = false
+sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[pipeline:main]
pipeline =
@@ -18,3 +17,37 @@ pipeline =
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, sqlalchemy
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
index 10fcd0cbc..e1baa2d64 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
@@ -1,20 +1,15 @@
from pyramid.configuration import Configurator
-from pyramid.settings import asbool
+from sqlalchemy import engine_from_config
from tutorial.models import initialize_sql
def main(global_config, **settings):
""" This function returns a WSGI application.
"""
- db_string = settings.get('db_string')
- if db_string is None:
- raise ValueError("No 'db_string' value in application configuration.")
- db_echo = settings.get('db_echo', 'false')
- initialize_sql(db_string, asbool(db_echo))
+ engine = engine_from_config(settings, 'sqlalchemy.')
+ initialize_sql(engine)
config = Configurator(settings=settings)
- config.begin()
config.add_static_view('static', 'tutorial:static')
config.add_route('home', '/', view='tutorial.views.my_view',
view_renderer='templates/mytemplate.pt')
- config.end()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki2/src/models/tutorial/models.py b/docs/tutorials/wiki2/src/models/tutorial/models.py
index ec9d2b25c..23b8afab8 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/models.py
@@ -1,6 +1,5 @@
import transaction
-from sqlalchemy import create_engine
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Text
@@ -27,8 +26,7 @@ class Page(Base):
self.name = name
self.data = data
-def initialize_sql(db_string, echo=False):
- engine = create_engine(db_string, echo=echo)
+def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
diff --git a/docs/tutorials/wiki2/src/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini
index e1d0ab598..23b01a338 100644
--- a/docs/tutorials/wiki2/src/views/development.ini
+++ b/docs/tutorials/wiki2/src/views/development.ini
@@ -5,8 +5,7 @@ debug_authorization = false
debug_notfound = false
debug_templates = true
default_locale_name = en
-db_string = sqlite:///%(here)s/tutorial.db
-db_echo = false
+sqlalchemy.url = sqlite:///%(here)s/tutorial.db
[pipeline:main]
pipeline =
@@ -18,3 +17,37 @@ pipeline =
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, sqlalchemy
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
index 9ef923e0b..91c299e24 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
@@ -1,29 +1,24 @@
from pyramid.configuration import Configurator
-from pyramid.settings import asbool
+from sqlalchemy import engine_from_config
from tutorial.models import initialize_sql
def main(global_config, **settings):
""" This function returns a WSGI application.
"""
- db_string = settings.get('db_string')
- if db_string is None:
- raise ValueError("No 'db_string' value in application configuration.")
- db_echo = settings.get('db_echo', 'false')
- initialize_sql(db_string, asbool(db_echo))
+ engine = engine_from_config(settings, 'sqlalchemy.')
+ initialize_sql(engine)
config = Configurator(settings=settings)
- config.begin()
config.add_static_view('static', 'tutorial:static')
config.add_route('home', '/', view='tutorial.views.view_wiki')
- config.add_route('view_page', '/:pagename',
+ config.add_route('view_page', '/{pagename}',
view='tutorial.views.view_page',
view_renderer='tutorial:templates/view.pt')
- config.add_route('add_page', '/add_page/:pagename',
+ config.add_route('add_page', '/add_page/{pagename}',
view='tutorial.views.add_page',
view_renderer='tutorial:templates/edit.pt')
- config.add_route('edit_page', '/:pagename/edit_page',
+ config.add_route('edit_page', '/{pagename}/edit_page',
view='tutorial.views.edit_page',
view_renderer='tutorial:templates/edit.pt')
- config.end()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki2/src/views/tutorial/models.py b/docs/tutorials/wiki2/src/views/tutorial/models.py
index ec9d2b25c..23b8afab8 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/models.py
@@ -1,6 +1,5 @@
import transaction
-from sqlalchemy import create_engine
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Text
@@ -27,8 +26,7 @@ class Page(Base):
self.name = name
self.data = data
-def initialize_sql(db_string, echo=False):
- engine = create_engine(db_string, echo=echo)
+def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
diff --git a/docs/tutorials/wiki2/src/views/tutorial/tests.py b/docs/tutorials/wiki2/src/views/tutorial/tests.py
index 40336fca4..435e4b588 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/tests.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/tests.py
@@ -14,9 +14,9 @@ def _initTestingDB():
return DBSession
def _registerRoutes(config):
- config.add_route('view_page', ':pagename')
- config.add_route('edit_page', ':pagename/edit_page')
- config.add_route('add_page', 'add_page/:pagename')
+ config.add_route('view_page', '{pagename}')
+ config.add_route('edit_page', '{pagename}/edit_page')
+ config.add_route('add_page', 'add_page/{pagename}')
class ViewWikiTests(unittest.TestCase):
def setUp(self):
@@ -28,7 +28,7 @@ class ViewWikiTests(unittest.TestCase):
def test_it(self):
from tutorial.views import view_wiki
- self.config.add_route('view_page', ':pagename')
+ self.config.add_route('view_page', '{pagename}')
request = testing.DummyRequest()
response = view_wiki(request)
self.assertEqual(response.location, 'http://example.com/FrontPage')
diff --git a/docs/tutorials/zodbsessions/index.rst b/docs/tutorials/zodbsessions/index.rst
deleted file mode 100644
index 9582e5de4..000000000
--- a/docs/tutorials/zodbsessions/index.rst
+++ /dev/null
@@ -1,189 +0,0 @@
-.. _zodb_sessions:
-
-Using ZODB-Based Sessions
-=========================
-
-Sessions are server-side namespaces which are associated with a site
-user that expire automatically after some period of disuse.
-
-If your application is ZODB-based (e.g. you've created an application
-from the ``bfg_zodb`` paster template, or you've followed the
-instructions in :ref:`zodb_with_zeo`), you can make use of the
-``repoze.session`` and ``repoze.browserid`` packages to add
-sessioning to your application.
-
-.. note:: You can use the ``repoze.session`` package even if your
- application is not ZODB-based, but its backing store requires ZODB,
- so it makes the most sense to use this package if your application
- already uses ZODB. This tutorial does not cover usage of
- ``repoze.session``-based sessions in applications that don't
- already use ZODB. For this, see `the standalone repoze.session
- usage documentation <http://docs.repoze.org/session/usage.html>`_.
- If you don't want to use ZODB to do sessioning, you might choose to
- use a relational/filestorage sessioning system such as `Beaker
- <http://pypi.python.org/pypi/Beaker>`_. :app:`Pyramid` is fully
- compatible with this system too.
-
-Installing Dependencies
------------------------
-
-#. Edit your :app:`Pyramid` application's ``setup.py`` file, adding
- the following packages to the ``install_requires`` of the
- application:
-
- - ``repoze.session``
-
- - ``repoze.browserid``
-
- For example, the relevant portion of your application's
- ``setup.py`` file might look like so when you're finished adding
- the dependencies.
-
- .. code-block:: python
- :linenos:
-
- setup(
- # ... other elements left out for brevity
- install_requires=[
- 'pyramid',
- 'repoze.folder',
- 'repoze.retry',
- 'repoze.tm2',
- 'repoze.zodbconn',
- 'repoze.session'
- 'repoze.browserid',
- ],
- # ... other elements left out for brevity
- )
-
-#. Rerun your application's ``setup.py`` file (e.g. using ``python
- setup.py develop``) to get these packages installed.
-
-Configuration
--------------
-
-#. Edit your application's Paste ``.ini`` file.
-
- If you already have an ``app`` section in the ``.ini`` file named
- ``main``, rename this section to ``myapp`` (e.g. ``app:main`` ->
- ``app:myapp``). Add a key to it named ``zodb_uri``, e.g.
-
- .. code-block:: python
- :linenos:
-
- [app:myapp]
- use = egg:myapp#app
- zodb_uri = zeo://%(here)s/zeo.sock
- reload_templates = true
- debug_authorization = false
- debug_notfound = false
-
- Add a ``filter`` section to the ``.ini`` file named "browserid":
-
- .. code-block:: python
- :linenos:
-
- [filter:browserid]
- use = egg:repoze.browserid#browserid
- secret_key = my-secret-key
-
- Replace ``my-secret-key`` with any random string. This string
- represents the value which the client-side "browser id" cookie is
- encrypted with, to prevent tampering.
-
- If a ``pipeline`` named ``main`` does not already exist in the
- paste ``.ini`` file , add a ``pipeline`` section named ``main``.
- Put the names ``connector``, ``egg:repoze.retry#retry``, and
- ``egg:repoze.tm2#tm`` to the top of the pipeline.
-
- .. code-block:: python
- :linenos:
-
- [pipeline:main]
- pipeline =
- browserid
- egg:repoze.retry#retry
- egg:repoze.tm2#tm
- myapp
-
- When you're finished, your ``.ini`` file might look like so:
-
- .. code-block:: ini
- :linenos:
-
- [DEFAULT]
- debug = true
-
- [app:myapp]
- use = egg:myapp#app
- zodb_uri = zeo://%(here)s/zeo.sock
- reload_templates = true
- debug_authorization = false
- debug_notfound = false
-
- [filter:browserid]
- use = egg:repoze.browserid#browserid
- secret_key = my-secret-key
-
- [pipeline:main]
- pipeline =
- browserid
- egg:repoze.retry#retry
- egg:repoze.tm2#tm
- myapp
-
- [server:main]
- use = egg:Paste#http
- host = 0.0.0.0
- port = 6543
-
- See :ref:`MyProject_ini` for more information about project Paste
- ``.ini`` files.
-
-#. Add a ``get_session`` API to your application. I've chosen to add
- it directly to my ``views.py`` file, although it can live anywhere.
-
- .. code-block:: python
- :linenos:
-
- from repoze.session.manager import SessionDataManager
- from pyramid.traversal import find_root
-
- def get_session(context, request):
- root = find_root(context)
- if not hasattr(root, '_sessions'):
- root._sessions = SessionDataManager(3600, 5)
- session = root._sessions.get(request.environ['repoze.browserid'])
- return session
-
- Note in the call to ``SessionDataManager`` that '3600' represents
- the disuse timeout (60 minutes == 3600 seconds), and '5' represents
- a write granularity time (the session will be marked as active at
- most every five seconds). Vary these values as necessary.
-
-#. Whenever you want to use a session in your application, call this API:
-
- .. code-block:: python
- :linenos:
-
- from repoze.session.manager import SessionDataManager
- from pyramid.traversal import find_root
- from pyramid.chameleon_zpt import render_template_to_response
-
- def my_view(context, request):
- session = get_session(context, request)
- session['abc'] = '123'
- return render_template_to_response('templates/mytemplate.pt',
- request = request,
- project = 'sess')
-
- def get_session(context, request):
- root = find_root(context)
- if not hasattr(root, '_sessions'):
- root._sessions = SessionDataManager(3600, 5)
- session = root._sessions.get(request.environ['repoze.browserid'])
- return session
-
-For more information, see the `repoze.session documentation
-<http://docs.repoze.org/session/>`_ and the `repoze.browserid
-documentation <http://pypi.python.org/pypi/repoze.browserid>`_.
diff --git a/docs/zcml/route.rst b/docs/zcml/route.rst
index ed849e3c1..c3bec72df 100644
--- a/docs/zcml/route.rst
+++ b/docs/zcml/route.rst
@@ -10,7 +10,7 @@ Attributes
~~~~~~~~~~
``pattern``
- The pattern of the route e.g. ``ideas/:idea``. This attribute is
+ The pattern of the route e.g. ``ideas/{idea}``. This attribute is
required. See :ref:`route_pattern_syntax` for information
about the syntax of route patterns.
@@ -51,9 +51,9 @@ Attributes
The syntax of the ``traverse`` argument is the same as it is for
``pattern``. For example, if the ``pattern`` provided to the
- ``route`` directive is ``articles/:article/edit``, and the
+ ``route`` directive is ``articles/{article}/edit``, and the
``traverse`` argument provided to the ``route`` directive is
- ``/:article``, when a request comes in that causes the route to
+ ``/{article}``, when a request comes in that causes the route to
match in such a way that the ``article`` match value is '1' (when
the request URI is ``/articles/1/edit``), the traversal path will be
generated as ``/1``. This means that the root object's
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index 4849d2c41..86d725bcf 100644
--- a/pyramid/authentication.py
+++ b/pyramid/authentication.py
@@ -286,6 +286,7 @@ EXPIRE = object()
class AuthTktCookieHelper(object):
auth_tkt = auth_tkt # for tests
+ now = None # for tests
userid_type_decoders = {
'int':int,
@@ -373,7 +374,10 @@ class AuthTktCookieHelper(object):
except self.auth_tkt.BadTicket:
return None
- now = time.time()
+ now = self.now # service tests
+
+ if now is None:
+ now = time.time()
if self.timeout and ( (timestamp + self.timeout) < now ):
return None
diff --git a/pyramid/chameleon_text.py b/pyramid/chameleon_text.py
index 4b97b2ba5..6eb7af4d0 100644
--- a/pyramid/chameleon_text.py
+++ b/pyramid/chameleon_text.py
@@ -1,5 +1,6 @@
import sys
+from zope.deprecation import deprecated
from zope.interface import implements
try:
@@ -21,13 +22,10 @@ except ImportError: # pragma: no cover
pass
from pyramid.interfaces import ITemplateRenderer
-from pyramid.interfaces import IChameleonTranslate
from pyramid.decorator import reify
from pyramid import renderers
from pyramid.path import caller_package
-from pyramid.settings import get_settings
-from pyramid.threadlocal import get_current_registry
class TextTemplateFile(TemplateFile):
default_parser = Parser()
@@ -39,35 +37,24 @@ class TextTemplateFile(TemplateFile):
super(TextTemplateFile, self).__init__(filename, parser, format,
doctype, **kwargs)
-def renderer_factory(path):
- return renderers.template_renderer_factory(path, TextTemplateRenderer)
+def renderer_factory(info):
+ return renderers.template_renderer_factory(info, TextTemplateRenderer)
class TextTemplateRenderer(object):
implements(ITemplateRenderer)
- def __init__(self, path):
+ def __init__(self, path, lookup):
self.path = path
+ self.lookup = lookup
@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')
- settings = get_settings()
- debug = False
- auto_reload = False
- if settings:
- # using .get here is a strategy to be kind to old *tests* rather
- # than being kind to any existing production system
- auto_reload = settings.get('reload_templates')
- debug = settings.get('debug_templates')
- reg = get_current_registry()
- translate = None
- if reg is not None:
- translate = reg.queryUtility(IChameleonTranslate)
return TextTemplateFile(self.path,
- auto_reload=auto_reload,
- debug=debug,
- translate=translate)
+ auto_reload=self.lookup.auto_reload,
+ debug=self.lookup.debug,
+ translate=self.lookup.translate)
def implementation(self):
return self.template
@@ -94,6 +81,11 @@ def get_renderer(path):
factory = renderers.RendererHelper(path, package=package)
return factory.get_renderer()
+deprecated(
+ 'get_renderer',
+ '(pyramid.chameleon_text.get_renderer is deprecated '
+ 'as of Pyramid 1.0; instead use pyramid.renderers.get_renderer)')
+
def get_template(path):
""" Return the underyling object representing a :term:`Chameleon`
text template using the template implied by the ``path`` argument.
@@ -108,6 +100,12 @@ def get_template(path):
factory = renderers.RendererHelper(path, package=package)
return factory.get_renderer().implementation()
+deprecated(
+ 'get_template',
+ '(pyramid.chameleon_text.get_template is deprecated '
+ 'as of Pyramid 1.0; instead use '
+ 'pyramid.renderers.get_renderer().implementation())')
+
def render_template(path, **kw):
""" Render a :term:`Chameleon` text template using the template
implied by the ``path`` argument. The ``path`` argument may be a
@@ -124,6 +122,11 @@ def render_template(path, **kw):
renderer = renderers.RendererHelper(path, package=package)
return renderer.render(kw, None, request=request)
+deprecated(
+ 'render_template',
+ '(pyramid.chameleon_text.render_template is deprecated '
+ 'as of Pyramid 1.0; instead use pyramid.renderers.render)')
+
def render_template_to_response(path, **kw):
""" Render a :term:`Chameleon` text template using the template
implied by the ``path`` argument. The ``path`` argument may be a
@@ -140,3 +143,9 @@ def render_template_to_response(path, **kw):
request = kw.pop('request', None)
renderer = renderers.RendererHelper(path, package=package)
return renderer.render_to_response(kw, None, request=request)
+
+deprecated(
+ 'render_template_to_response',
+ '(pyramid.chameleon_text.render_template_to_response is deprecated '
+ 'as of Pyramid 1.0; instead use pyramid.renderers.render_to_response)')
+
diff --git a/pyramid/chameleon_zpt.py b/pyramid/chameleon_zpt.py
index d5cc20262..5c2d6f70f 100644
--- a/pyramid/chameleon_zpt.py
+++ b/pyramid/chameleon_zpt.py
@@ -1,5 +1,7 @@
import sys
+import threading
+from zope.deprecation import deprecated
from zope.interface import implements
try:
@@ -12,44 +14,32 @@ except ImportError: # pragma: no cover
def __init__(self, *arg, **kw):
raise ImportError, exc, tb
-from pyramid.interfaces import IChameleonTranslate
from pyramid.interfaces import ITemplateRenderer
from pyramid.decorator import reify
from pyramid.path import caller_package
from pyramid import renderers
-from pyramid.settings import get_settings
-from pyramid.threadlocal import get_current_registry
-def renderer_factory(path):
- return renderers.template_renderer_factory(path, ZPTTemplateRenderer)
+registry_lock = threading.Lock()
+
+def renderer_factory(info, lock=registry_lock):
+ return renderers.template_renderer_factory(info, ZPTTemplateRenderer)
class ZPTTemplateRenderer(object):
implements(ITemplateRenderer)
- def __init__(self, path):
+ def __init__(self, path, lookup):
self.path = path
+ self.lookup = lookup
@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')
- settings = get_settings()
- debug = False
- auto_reload = False
- if settings:
- # using .get here is a strategy to be kind to old *tests* rather
- # than being kind to any existing production system
- auto_reload = settings.get('reload_templates')
- debug = settings.get('debug_templates')
- reg = get_current_registry()
- translate = None
- if reg is not None:
- translate = reg.queryUtility(IChameleonTranslate)
return PageTemplateFile(self.path,
- auto_reload=auto_reload,
- debug=debug,
- translate=translate)
+ auto_reload=self.lookup.auto_reload,
+ debug=self.lookup.debug,
+ translate=self.lookup.translate)
def implementation(self):
return self.template
@@ -76,6 +66,11 @@ def get_renderer(path):
factory = renderers.RendererHelper(name=path, package=package)
return factory.get_renderer()
+deprecated(
+ 'get_renderer',
+ '(pyramid.chameleon_zpt.get_renderer is deprecated '
+ 'as of Pyramid 1.0; instead use pyramid.renderers.get_renderer)')
+
def get_template(path):
""" Return the underyling object representing a :term:`Chameleon`
ZPT template using the template implied by the ``path`` argument.
@@ -90,6 +85,12 @@ def get_template(path):
factory = renderers.RendererHelper(name=path, package=package)
return factory.get_renderer().implementation()
+deprecated(
+ 'get_template',
+ '(pyramid.chameleon_zpt.get_template is deprecated '
+ 'as of Pyramid 1.0; instead use '
+ 'pyramid.renderers.get_renderer().implementation())')
+
def render_template(path, **kw):
""" Render a :term:`Chameleon` ZPT template using the template
implied by the ``path`` argument. The ``path`` argument may be a
@@ -106,6 +107,11 @@ def render_template(path, **kw):
renderer = renderers.RendererHelper(name=path, package=package)
return renderer.render(kw, None, request=request)
+deprecated(
+ 'render_template',
+ '(pyramid.chameleon_zpt.render_template is deprecated as of Pyramid 1.0; '
+ 'instead use pyramid.renderers.render)')
+
def render_template_to_response(path, **kw):
""" Render a :term:`Chameleon` ZPT template using the template
implied by the ``path`` argument. The ``path`` argument may be a
@@ -122,3 +128,8 @@ def render_template_to_response(path, **kw):
request = kw.pop('request', None)
renderer = renderers.RendererHelper(name=path, package=package)
return renderer.render_to_response(kw, None, request=request)
+
+deprecated(
+ 'render_template_to_response',
+ '(pyramid.chameleon_zpt.render_template_to_response is deprecated; as of '
+ 'Pyramid 1.0, instead use pyramid.renderers.render_to_response)')
diff --git a/pyramid/compat/__init__.py b/pyramid/compat/__init__.py
index 205175132..8bbf79703 100644
--- a/pyramid/compat/__init__.py
+++ b/pyramid/compat/__init__.py
@@ -133,7 +133,12 @@ except NameError: # pragma: no cover
try:
import json
except ImportError: # pragma: no cover
- import simplejson as json
+ try:
+ import simplejson as json
+ except NotImplementedError:
+ from django.utils import simplejson as json # GAE
+
+
try:
from hashlib import md5
diff --git a/pyramid/configuration.py b/pyramid/configuration.py
index cee65a982..29a322c7f 100644
--- a/pyramid/configuration.py
+++ b/pyramid/configuration.py
@@ -3,7 +3,6 @@ import re
import sys
import threading
import inspect
-import pkg_resources
import venusian
@@ -24,6 +23,8 @@ from pyramid.interfaces import IChameleonTranslate
from pyramid.interfaces import IDebugLogger
from pyramid.interfaces import IDefaultPermission
from pyramid.interfaces import IDefaultRootFactory
+from pyramid.interfaces import IException
+from pyramid.interfaces import IExceptionResponse
from pyramid.interfaces import IExceptionViewClassifier
from pyramid.interfaces import ILocaleNegotiator
from pyramid.interfaces import IMultiView
@@ -36,34 +37,38 @@ from pyramid.interfaces import IRootFactory
from pyramid.interfaces import IRouteRequest
from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import ISecuredView
+from pyramid.interfaces import ISessionFactory
from pyramid.interfaces import IStaticURLInfo
from pyramid.interfaces import ITranslationDirectories
from pyramid.interfaces import ITraverser
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
-from pyramid.interfaces import IExceptionResponse
-from pyramid.interfaces import IException
-from pyramid.interfaces import ISessionFactory
-from pyramid import chameleon_text
-from pyramid import chameleon_zpt
-from pyramid.mako_templating import renderer_factory as mako_renderer_factory
+try:
+ from pyramid import chameleon_text
+except TypeError: # pragma: no cover
+ chameleon_text = None # pypy
+try:
+ from pyramid import chameleon_zpt
+except TypeError: # pragma: no cover
+ chameleon_zpt = None # pypy
+
from pyramid import renderers
-from pyramid.renderers import RendererHelper
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.compat import all
from pyramid.compat import md5
from pyramid.events import ApplicationCreated
+from pyramid.exceptions import ConfigurationError
from pyramid.exceptions import Forbidden
from pyramid.exceptions import NotFound
from pyramid.exceptions import PredicateMismatch
-from pyramid.exceptions import ConfigurationError
from pyramid.i18n import get_localizer
from pyramid.log import make_stream_logger
+from pyramid.mako_templating import renderer_factory as mako_renderer_factory
from pyramid.path import caller_package
from pyramid.path import package_path
-from pyramid.path import package_of
from pyramid.registry import Registry
+from pyramid.renderers import RendererHelper
from pyramid.request import route_request_iface
from pyramid.resource import PackageOverrides
from pyramid.resource import resolve_resource_spec
@@ -72,25 +77,30 @@ from pyramid.static import StaticURLInfo
from pyramid.threadlocal import get_current_registry
from pyramid.threadlocal import get_current_request
from pyramid.threadlocal import manager
-from pyramid.traversal import traversal_path
from pyramid.traversal import DefaultRootFactory
from pyramid.traversal import find_interface
+from pyramid.traversal import traversal_path
from pyramid.urldispatch import RoutesMapper
-from pyramid.view import render_view_to_response
+from pyramid.util import DottedNameResolver
from pyramid.view import default_exceptionresponse_view
+from pyramid.view import render_view_to_response
MAX_ORDER = 1 << 30
DEFAULT_PHASH = md5().hexdigest()
DEFAULT_RENDERERS = (
- ('.pt', chameleon_zpt.renderer_factory),
- ('.txt', chameleon_text.renderer_factory),
('.mak', mako_renderer_factory),
('.mako', mako_renderer_factory),
('json', renderers.json_renderer_factory),
('string', renderers.string_renderer_factory),
)
+if chameleon_text:
+ DEFAULT_RENDERERS += (('.pt', chameleon_zpt.renderer_factory),)
+if chameleon_zpt:
+ DEFAULT_RENDERERS += (('.txt', chameleon_text.renderer_factory),)
+
+
class Configurator(object):
"""
A Configurator is used to configure a :app:`Pyramid`
@@ -285,12 +295,17 @@ class Configurator(object):
attr=None, renderer=None, wrapper_viewname=None,
viewname=None, accept=None, order=MAX_ORDER,
phash=DEFAULT_PHASH):
+ if renderer is None: # use default renderer if one exists
+ default_renderer_factory = self.registry.queryUtility(
+ IRendererFactory)
+ if default_renderer_factory is not None:
+ renderer = {'name':None, 'package':self.package}
view = self.maybe_dotted(view)
authn_policy = self.registry.queryUtility(IAuthenticationPolicy)
authz_policy = self.registry.queryUtility(IAuthorizationPolicy)
settings = self.registry.settings
logger = self.registry.queryUtility(IDebugLogger)
- mapped_view = _map_view(view, attr, renderer, self.registry)
+ mapped_view = _map_view(view, self.registry, attr, renderer)
owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname)
secured_view = _secure_view(owrapped_view, permission,
authn_policy, authz_policy)
@@ -570,11 +585,10 @@ class Configurator(object):
return subscriber
def add_settings(self, settings=None, **kw):
- """Augment the ``settings`` argument passed in to the
- Configurator constructor with one or more 'setting' key/value
- pairs. A setting is a single key/value pair in the
- dictionary-ish object returned from the API
- :func:`pyramid.settings.get_settings` and
+ """Augment the ``settings`` argument passed in to the Configurator
+ constructor with one or more 'setting' key/value pairs. A setting is
+ a single key/value pair in the dictionary-ish object returned from
+ the API :attr:`pyramid.registry.Registry.settings` and
:meth:`pyramid.configuration.Configurator.get_settings`.
You may pass a dictionary::
@@ -585,11 +599,10 @@ class Configurator(object):
config.add_settings(external_uri='http://example.com')
- This function is useful when you need to test code that calls the
- :func:`pyramid.settings.get_settings` API (or the
- :meth:`pyramid.configuration.Configurator.get_settings` API or
- accesses ``request.settings``) and which uses return values from that
- API.
+ This function is useful when you need to test code that accesses the
+ :attr:`pyramid.registry.Registry.settings` API (or the
+ :meth:`pyramid.configuration.Configurator.get_settings` API) and
+ which uses values from that API.
"""
if settings is None:
settings = {}
@@ -610,9 +623,8 @@ class Configurator(object):
.. note:: For backwards compatibility, dictionary keys can also be
looked up as attributes of the settings object.
- .. note:: the :class:`pyramid.settings.get_settings` and function
- performs the same duty and the settings attribute can also be
- accessed as :attr:`pyramid.registry.Registry.settings`
+ .. note:: the :attr:`pyramid.registry.Registry.settings` API
+ performs the same duty.
"""
return self.registry.settings
@@ -691,6 +703,8 @@ class Configurator(object):
Any extra keyword arguments are passed along to ``add_route``.
+ See :ref:`handlers_chapter` for more explanatory documentation.
+
This method returns the result of add_route."""
handler = self.maybe_dotted(handler)
@@ -1252,9 +1266,9 @@ class Configurator(object):
The syntax of the ``traverse`` argument is the same as it is
for ``pattern``. For example, if the ``pattern`` provided to
- ``add_route`` is ``articles/:article/edit``, and the
+ ``add_route`` is ``articles/{article}/edit``, and the
``traverse`` argument provided to ``add_route`` is
- ``/:article``, when a request comes in that causes the route
+ ``/{article}``, when a request comes in that causes the route
to match in such a way that the ``article`` match value is
'1' (when the request URI is ``/articles/1/edit``), the
traversal path will be generated as ``/1``. This means that
@@ -1298,7 +1312,7 @@ class Configurator(object):
pattern
- The pattern of the route e.g. ``ideas/:idea``. This
+ The pattern of the route e.g. ``ideas/{idea}``. This
argument is required. See :ref:`route_path_pattern_syntax`
for information about the syntax of route patterns. If the
pattern doesn't match the current URL, route matching
@@ -1309,7 +1323,7 @@ class Configurator(object):
to this function will be used to represent the pattern
value if the ``pattern`` argument is ``None``. If both
``path`` and ``pattern`` are passed, ``pattern`` wins.
-
+
xhr
This value should be either ``True`` or ``False``. If this
@@ -1562,7 +1576,7 @@ class Configurator(object):
By default, ``categories`` is ``None`` which will execute
*all* Venusian decorator callbacks including
:app:`Pyramid`-related decorators such as
- :class:`pyramid.view.view_config``. If this is not desirable
+ :class:`pyramid.view.view_config`. If this is not desirable
because the codebase has other Venusian-using decorators that
aren't meant to be invoked during a particular scan, use
``('pyramid',)`` as a ``categories`` value to limit the execution
@@ -1583,7 +1597,9 @@ class Configurator(object):
Add a :app:`Pyramid` :term:`renderer` factory to the
current configuration state.
- The ``name`` argument is the renderer name.
+ The ``name`` argument is the renderer name. Use ``None`` to
+ represent the default renderer (a renderer which will be used for all
+ views unless they name another renderer specifically).
The ``factory`` argument is Python reference to an
implementation of a :term:`renderer` factory or a
@@ -1597,6 +1613,11 @@ class Configurator(object):
to use this method.
"""
factory = self.maybe_dotted(factory)
+ # if name is None or the empty string, we're trying to register
+ # a default renderer, but registerUtility is too dumb to accept None
+ # as a name
+ if not name:
+ name = ''
self.registry.registerUtility(
factory, IRendererFactory, name=name, info=_info)
@@ -2418,7 +2439,7 @@ def is_response(ob):
return True
return False
-def _map_view(view, attr=None, renderer=None, registry=None):
+def _map_view(view, registry, attr=None, renderer=None):
wrapped_view = view
helper = None
@@ -2705,148 +2726,6 @@ def make_app(root_factory, package=None, filename='configure.zcml',
config.end()
return config.make_wsgi_app()
-class DottedNameResolver(object):
- """ This class resolves dotted name references to 'global' Python
- objects (objects which can be imported) to those objects.
-
- Two dotted name styles are supported during deserialization:
-
- - ``pkg_resources``-style dotted names where non-module attributes
- of a package are separated from the rest of the path using a ':'
- e.g. ``package.module:attr``.
-
- - ``zope.dottedname``-style dotted names where non-module
- attributes of a package are separated from the rest of the path
- using a '.' e.g. ``package.module.attr``.
-
- These styles can be used interchangeably. If the serialization
- contains a ``:`` (colon), the ``pkg_resources`` resolution
- mechanism will be chosen, otherwise the ``zope.dottedname``
- resolution mechanism will be chosen.
-
- The constructor accepts a single argument named ``package`` which
- should be a one of:
-
- - a Python module or package object
-
- - A fully qualified (not relative) dotted name to a module or package
-
- - The value ``None``
-
- The ``package`` is used when relative dotted names are supplied to
- the resolver's ``resolve`` and ``maybe_resolve`` methods. A
- dotted name which has a ``.`` (dot) or ``:`` (colon) as its first
- character is treated as relative.
-
- If the value ``None`` is supplied as the package name, the
- resolver will only be able to resolve fully qualified (not
- relative) names. Any attempt to resolve a relative name when the
- ``package`` is ``None`` will result in an
- :exc:`pyramid.configuration.ConfigurationError` exception.
-
- If a *module* or *module name* (as opposed to a package or package
- name) is supplied as ``package``, its containing package is
- computed and this package used to derive the package name (all
- names are resolved relative to packages, never to modules). For
- example, if the ``package`` argument to this type was passed the
- string ``xml.dom.expatbuilder``, and ``.mindom`` is supplied to
- the ``resolve`` method, the resulting import would be for
- ``xml.minidom``, because ``xml.dom.expatbuilder`` is a module
- object, not a package object.
-
- If a *package* or *package name* (as opposed to a module or module
- name) is supplied as ``package``, this package will be used to
- relative compute dotted names. For example, if the ``package``
- argument to this type was passed the string ``xml.dom``, and
- ``.minidom`` is supplied to the ``resolve`` method, the resulting
- import would be for ``xml.minidom``.
-
- When a dotted name cannot be resolved, a
- :class:`pyramid.exceptions.ConfigurationError` error is raised.
- """
- def __init__(self, package):
- if package is None:
- self.package_name = None
- self.package = None
- else:
- if isinstance(package, basestring):
- try:
- __import__(package)
- except ImportError:
- raise ConfigurationError(
- 'The dotted name %r cannot be imported' % (package,))
- package = sys.modules[package]
- self.package = package_of(package)
- self.package_name = self.package.__name__
-
- def _pkg_resources_style(self, value):
- """ package.module:attr style """
- if value.startswith('.') or value.startswith(':'):
- if not self.package_name:
- raise ConfigurationError(
- 'relative name %r irresolveable without '
- 'package_name' % (value,))
- if value in ['.', ':']:
- value = self.package_name
- else:
- value = self.package_name + value
- return pkg_resources.EntryPoint.parse(
- 'x=%s' % value).load(False)
-
- def _zope_dottedname_style(self, value):
- """ package.module.attr style """
- module = self.package_name and self.package_name or None
- if value == '.':
- if self.package_name is None:
- raise ConfigurationError(
- 'relative name %r irresolveable without package' % (value,)
- )
- name = module.split('.')
- else:
- name = value.split('.')
- if not name[0]:
- if module is None:
- raise ConfigurationError(
- 'relative name %r irresolveable without '
- 'package' % (value,)
- )
- module = module.split('.')
- name.pop(0)
- while not name[0]:
- module.pop()
- name.pop(0)
- name = module + name
-
- used = name.pop(0)
- found = __import__(used)
- for n in name:
- used += '.' + n
- try:
- found = getattr(found, n)
- except AttributeError:
- __import__(used)
- found = getattr(found, n) # pragma: no cover
-
- return found
-
- def resolve(self, dotted):
- if not isinstance(dotted, basestring):
- raise ConfigurationError('%r is not a string' % (dotted,))
- return self.maybe_resolve(dotted)
-
- def maybe_resolve(self, dotted):
- if isinstance(dotted, basestring):
- try:
- if ':' in dotted:
- return self._pkg_resources_style(dotted)
- else:
- return self._zope_dottedname_style(dotted)
- except ImportError:
- raise ConfigurationError(
- 'The dotted name %r cannot be imported' % (dotted,))
- return dotted
-
-
class ActionPredicate(object):
action_name = 'action'
def __init__(self, action):
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index 9835f8f7d..f021ba60b 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -100,6 +100,26 @@ class IBeforeRender(Interface):
""" Return the value for key ``k`` from the renderer globals
dictionary, or the default if no such value exists."""
+class IRenderer(Interface):
+ def __call__(value, system):
+ """ Call a the renderer implementation with the result of the
+ view (``value``) passed in and return a result (a string or
+ unicode object useful as a response body). Values computed by
+ the system are passed by the system in the ``system``
+ parameter, which is a dictionary. Keys in the dictionary
+ include: ``view`` (the view callable that returned the value),
+ ``renderer_name`` (the template name or simple name of the
+ renderer), ``context`` (the context object passed to the
+ view), and ``request`` (the request object passed to the
+ view)."""
+
+class ITemplateRenderer(IRenderer):
+ def implementation():
+ """ Return the object that the underlying templating system
+ uses to render the template; it is typically a callable that
+ accepts arbitrary keyword arguments and returns a string or
+ unicode object """
+
# internal interfaces
class IRequest(Interface):
@@ -166,7 +186,7 @@ class IRequestFactory(Interface):
def blank(path):
""" Return an empty request object (see
- :meth:`pyramid.request.Request.blank``)"""
+ :meth:`pyramid.request.Request.blank`)"""
class IViewClassifier(Interface):
""" *Internal only* marker interface for views."""
@@ -233,19 +253,6 @@ class ITraverser(Interface):
ITraverserFactory = ITraverser # b / c for 1.0 code
-class IRenderer(Interface):
- def __call__(value, system):
- """ Call a the renderer implementation with the result of the
- view (``value``) passed in and return a result (a string or
- unicode object useful as a response body). Values computed by
- the system are passed by the system in the ``system``
- parameter, which is a dictionary. Keys in the dictionary
- include: ``view`` (the view callable that returned the value),
- ``renderer_name`` (the template name or simple name of the
- renderer), ``context`` (the context object passed to the
- view), and ``request`` (the request object passed to the
- view)."""
-
class IRendererFactory(Interface):
def __call__(name):
""" Return an object that implements ``IRenderer`` """
@@ -259,13 +266,6 @@ class IRendererGlobalsFactory(Interface):
``renderer_name``, which will be the name of the renderer in
use."""
-class ITemplateRenderer(IRenderer):
- def implementation():
- """ Return the object that the underlying templating system
- uses to render the template; it is typically a callable that
- accepts arbitrary keyword arguments and returns a string or
- unicode object """
-
class IViewPermission(Interface):
def __call__(context, request):
""" Return True if the permission allows, return False if it denies. """
@@ -392,6 +392,13 @@ class IPackageOverrides(Interface):
# traversalwrapper)
VH_ROOT_KEY = 'HTTP_X_VHM_ROOT'
+class IChameleonLookup(Interface):
+ translate = Attribute('IChameleonTranslate object')
+ debug = Attribute('The ``debug_templates`` setting for this application')
+ auto_reload = Attribute('The ``reload_templates`` setting for this app')
+ def __call__(self, info):
+ """ Return an ITemplateRenderer based on IRendererInfo ``info`` """
+
class IChameleonTranslate(Interface):
""" Internal interface representing a chameleon translate function """
def __call__(msgid, domain=None, mapping=None, context=None,
diff --git a/pyramid/mako_templating.py b/pyramid/mako_templating.py
index bf51257a0..a5f9b0f68 100644
--- a/pyramid/mako_templating.py
+++ b/pyramid/mako_templating.py
@@ -1,4 +1,5 @@
import os
+import threading
from zope.interface import implements
from zope.interface import Interface
@@ -7,6 +8,8 @@ from pyramid.interfaces import ITemplateRenderer
from pyramid.exceptions import ConfigurationError
from pyramid.resource import resolve_resource_spec
from pyramid.resource import abspath_from_resource_spec
+from pyramid.settings import asbool
+from pyramid.util import DottedNameResolver
from mako.lookup import TemplateLookup
from mako import exceptions
@@ -51,6 +54,8 @@ class PkgResourceTemplateLookup(TemplateLookup):
return TemplateLookup.get_template(self, uri)
+registry_lock = threading.Lock()
+
def renderer_factory(info):
path = info.name
registry = info.registry
@@ -58,19 +63,46 @@ def renderer_factory(info):
lookup = registry.queryUtility(IMakoLookup)
if lookup is None:
reload_templates = settings.get('reload_templates', False)
- directories = settings.get('mako.directories')
- module_directory = settings.get('mako.module_directory')
+ directories = settings.get('mako.directories', None)
+ module_directory = settings.get('mako.module_directory', None)
input_encoding = settings.get('mako.input_encoding', 'utf-8')
+ error_handler = settings.get('mako.error_handler', None)
+ default_filters = settings.get('mako.default_filters', None)
+ imports = settings.get('mako.imports', None)
+ strict_undefined = settings.get('mako.strict_undefined', 'false')
if directories is None:
raise ConfigurationError(
'Mako template used without a ``mako.directories`` setting')
- directories = directories.splitlines()
+ if not hasattr(directories, '__iter__'):
+ directories = filter(None, directories.splitlines())
directories = [ abspath_from_resource_spec(d) for d in directories ]
+ if module_directory is not None:
+ module_directory = abspath_from_resource_spec(module_directory)
+ if error_handler is not None:
+ dotted = DottedNameResolver(info.package)
+ error_handler = dotted.maybe_resolve(error_handler)
+ if default_filters is not None:
+ if not hasattr(default_filters, '__iter__'):
+ default_filters = filter(None, default_filters.splitlines())
+ if imports is not None:
+ if not hasattr(imports, '__iter__'):
+ imports = filter(None, imports.splitlines())
+ strict_undefined = asbool(strict_undefined)
+
lookup = PkgResourceTemplateLookup(directories=directories,
module_directory=module_directory,
input_encoding=input_encoding,
- filesystem_checks=reload_templates)
- registry.registerUtility(lookup, IMakoLookup)
+ error_handler=error_handler,
+ default_filters=default_filters,
+ imports=imports,
+ filesystem_checks=reload_templates,
+ strict_undefined=strict_undefined)
+ registry_lock.acquire()
+ try:
+ registry.registerUtility(lookup, IMakoLookup)
+ finally:
+ registry_lock.release()
+
return MakoLookupTemplateRenderer(path, lookup)
@@ -81,12 +113,8 @@ class MakoLookupTemplateRenderer(object):
self.lookup = lookup
def implementation(self):
- return self.template
-
- @property
- def template(self):
return self.lookup.get_template(self.path)
-
+
def __call__(self, value, system):
context = system.pop('context', None)
if context is not None:
@@ -98,7 +126,7 @@ class MakoLookupTemplateRenderer(object):
system.update(value)
except (TypeError, ValueError):
raise ValueError('renderer was passed non-dictionary as value')
- template = self.template
+ template = self.implementation()
if def_name is not None:
template = template.get_def(def_name)
result = template.render_unicode(**system)
diff --git a/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl b/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl
index a245bf141..f6591ab32 100755
--- a/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl
+++ b/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl
@@ -1,18 +1,14 @@
from pyramid.configuration import Configurator
-from pramid.settings import asbool
+from sqlalchemy import engine_from_config
from {{package}}.models import appmaker
def main(global_config, **settings):
""" This function returns a WSGI application.
"""
- db_string = settings.get('db_string')
- if db_string is None:
- raise ValueError("No 'db_string' value in application configuration.")
- db_echo = settings.get('db_echo', 'false')
- get_root = appmaker(db_string, asbool(db_echo))
+ engine = engine_from_config(settings, 'sqlalchemy.')
+ get_root = appmaker(engine)
config = Configurator(settings=settings, root_factory=get_root)
- config.begin()
config.add_static_view('static', '{{package}}:static')
config.add_view('{{package}}.views.view_root',
context='{{package}}.models.MyApp',
@@ -20,7 +16,6 @@ def main(global_config, **settings):
config.add_view('{{package}}.views.view_model',
context='{{package}}.models.MyModel',
renderer="templates/model.pt")
- config.end()
return config.make_wsgi_app()
diff --git a/pyramid/paster_templates/alchemy/+package+/models.py b/pyramid/paster_templates/alchemy/+package+/models.py
index b0a00b648..9942f2bb0 100755
--- a/pyramid/paster_templates/alchemy/+package+/models.py
+++ b/pyramid/paster_templates/alchemy/+package+/models.py
@@ -73,8 +73,7 @@ def populate():
session.flush()
transaction.commit()
-def initialize_sql(db_string, db_echo=False):
- engine = create_engine(db_string, echo=db_echo)
+def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
@@ -83,6 +82,6 @@ def initialize_sql(db_string, db_echo=False):
except IntegrityError:
DBSession.rollback()
-def appmaker(db_string, db_echo=False):
- initialize_sql(db_string, db_echo)
+def appmaker(engine):
+ initialize_sql(engine)
return default_get_root
diff --git a/pyramid/paster_templates/alchemy/development.ini_tmpl b/pyramid/paster_templates/alchemy/development.ini_tmpl
index de4605e33..4f4b98ac0 100644
--- a/pyramid/paster_templates/alchemy/development.ini_tmpl
+++ b/pyramid/paster_templates/alchemy/development.ini_tmpl
@@ -5,8 +5,7 @@ debug_authorization = false
debug_notfound = false
debug_templates = true
default_locale_name = en
-db_string = sqlite:///%(here)s/{{package}}.db
-db_echo = false
+sqlalchemy.url = sqlite:///%(here)s/{{project}}.db
[pipeline:main]
pipeline =
@@ -18,3 +17,37 @@ pipeline =
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, sqlalchemy
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl
index 6a8beb1a7..9f3793538 100644
--- a/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl
+++ b/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl
@@ -5,15 +5,13 @@ def main(global_config, **settings):
"""
from pyramid.configuration import Configurator
config = Configurator(settings=settings)
- config.begin()
session_factory = session_factory_from_settings(settings)
config.set_session_factory(session_factory)
config.add_static_view('static', '{{package}}:static/')
- config.add_handler('action', '/:action',
+ config.add_handler('action', '/{action}',
'{{package}}.handlers.hello:HelloHandler')
config.add_handler('home', '/', '{{package}}.handlers.hello:HelloHandler',
action='index')
config.add_subscriber('{{package}}.lib.subscribers.add_renderer_globals',
'pyramid.events.BeforeRender')
- config.end()
return config.make_wsgi_app()
diff --git a/pyramid/paster_templates/pylons_basic/development.ini_tmpl b/pyramid/paster_templates/pylons_basic/development.ini_tmpl
index 569256739..b14ecc7e2 100644
--- a/pyramid/paster_templates/pylons_basic/development.ini_tmpl
+++ b/pyramid/paster_templates/pylons_basic/development.ini_tmpl
@@ -13,10 +13,37 @@ session.key = {{project}}
session.secret = {{random_string}}
[pipeline:main]
-pipeline = egg:WebError#evalerror
- {{project}}
+pipeline =
+ egg:WebError#evalerror
+ {{project}}
[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl
index a1a353aae..5e284ec88 100644
--- a/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl
+++ b/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl
@@ -5,15 +5,13 @@ def main(global_config, **settings):
"""
from pyramid.configuration import Configurator
config = Configurator(settings=settings)
- config.begin()
session_factory = session_factory_from_settings(settings)
config.set_session_factory(session_factory)
config.add_static_view('static', '{{package}}:static/')
- config.add_handler('action', '/:action', '{{package}}.handlers:MyHandler')
+ config.add_handler('action', '/{action}', '{{package}}.handlers:MyHandler')
config.add_handler('home', '/', '{{package}}.handlers:MyHandler',
action='index')
config.add_subscriber('{{package}}.subscribers.add_renderer_globals',
'pyramid.events.BeforeRender')
- config.end()
return config.make_wsgi_app()
diff --git a/pyramid/paster_templates/pylons_minimal/development.ini_tmpl b/pyramid/paster_templates/pylons_minimal/development.ini_tmpl
index 569256739..d31cd1ca2 100644
--- a/pyramid/paster_templates/pylons_minimal/development.ini_tmpl
+++ b/pyramid/paster_templates/pylons_minimal/development.ini_tmpl
@@ -20,3 +20,29 @@ pipeline = egg:WebError#evalerror
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl
index a8df81fd6..b67a2c35b 100644
--- a/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl
+++ b/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl
@@ -1,26 +1,23 @@
from pyramid.configuration import Configurator
-from pyramid.settings import asbool
from pyramid_beaker import session_factory_from_settings
+from sqlalchemy import engine_from_config
+
+from {{package}}.models import initialize_sql
+
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
- from {{package}}.models import initialize_sql
- db_string = settings.get('db_string')
- if db_string is None:
- raise ValueError("No 'db_string' value in application "
- "configuration.")
- initialize_sql(db_string, asbool(settings.get('db_echo')))
+ engine = engine_from_config(settings, 'sqlalchemy.')
+ initialize_sql(engine)
config = Configurator(settings=settings)
- config.begin()
session_factory = session_factory_from_settings(settings)
config.set_session_factory(session_factory)
config.add_static_view('static', '{{package}}:static/')
- config.add_handler('main', '/:action', '{{package}}.handlers:MyHandler')
+ config.add_handler('main', '/{action}', '{{package}}.handlers:MyHandler')
config.add_handler('home', '/', '{{package}}.handlers:MyHandler',
action='index')
config.add_subscriber('{{package}}.subscribers.add_renderer_globals',
'pyramid.events.BeforeRender')
- config.end()
return config.make_wsgi_app()
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/models.py b/pyramid/paster_templates/pylons_sqla/+package+/models.py
index e1fc7f43c..181232072 100644
--- a/pyramid/paster_templates/pylons_sqla/+package+/models.py
+++ b/pyramid/paster_templates/pylons_sqla/+package+/models.py
@@ -36,8 +36,7 @@ def populate():
DBSession.flush()
transaction.commit()
-def initialize_sql(db_string, db_echo=False):
- engine = create_engine(db_string, echo=db_echo)
+def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
diff --git a/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl b/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl
index 099df47bd..b55760b37 100644
--- a/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl
+++ b/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl
@@ -3,8 +3,9 @@ import unittest
class MyHandlerTests(unittest.TestCase):
def setUp(self):
from pyramid.configuration import Configurator
+ from sqlalchemy import create_engine
from {{package}}.models import initialize_sql
- self.session = initialize_sql('sqlite://')
+ self.session = initialize_sql(create_engine('sqlite://'))
self.config = Configurator()
self.config.begin()
diff --git a/pyramid/paster_templates/pylons_sqla/development.ini_tmpl b/pyramid/paster_templates/pylons_sqla/development.ini_tmpl
index 936d41b29..1cc1a42e4 100644
--- a/pyramid/paster_templates/pylons_sqla/development.ini_tmpl
+++ b/pyramid/paster_templates/pylons_sqla/development.ini_tmpl
@@ -1,13 +1,12 @@
[app:{{project}}]
use = egg:{{project}}
reload_templates = true
-mako.directories = {{package}}:templates
debug_authorization = false
debug_notfound = false
debug_templates = true
default_locale_name = en
-db_string = sqlite:///%(here)s/tutorial.db
-db_echo = true
+mako.directories = {{package}}:templates
+sqlalchemy.url = sqlite:///%(here)s/{{project}}.db
session.type = file
session.data_dir = %(here)s/data/sessions/data
session.lock_dir = %(here)s/data/sessions/lock
@@ -24,3 +23,37 @@ pipeline =
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, sqlalchemy
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl
index 8b13e3590..5c326caa8 100644
--- a/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl
+++ b/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl
@@ -1,22 +1,17 @@
from pyramid.configuration import Configurator
-from pyramid.settings import asbool
+from sqlalchemy import engine_from_config
from {{package}}.models import initialize_sql
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
- db_string = settings.get('db_string')
- if db_string is None:
- raise ValueError("No 'db_string' value in application configuration.")
- db_echo = settings.get('db_echo', 'false')
- initialize_sql(db_string, asbool(db_echo))
+ engine = engine_from_config(settings, 'sqlalchemy.')
+ initialize_sql(engine)
config = Configurator(settings=settings)
- config.begin()
config.add_static_view('static', '{{package}}:static')
config.add_route('home', '/', view='{{package}}.views.my_view',
view_renderer='templates/mytemplate.pt')
- config.end()
return config.make_wsgi_app()
diff --git a/pyramid/paster_templates/routesalchemy/+package+/models.py b/pyramid/paster_templates/routesalchemy/+package+/models.py
index fd439a4c2..e8755ba78 100644
--- a/pyramid/paster_templates/routesalchemy/+package+/models.py
+++ b/pyramid/paster_templates/routesalchemy/+package+/models.py
@@ -1,6 +1,5 @@
import transaction
-from sqlalchemy import create_engine
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Unicode
@@ -33,8 +32,7 @@ def populate():
session.flush()
transaction.commit()
-def initialize_sql(db_string, db_echo=False):
- engine = create_engine(db_string, echo=db_echo)
+def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
diff --git a/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl
index de75e5df0..cfab37670 100644
--- a/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl
+++ b/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl
@@ -3,8 +3,9 @@ from pyramid.configuration import Configurator
from pyramid import testing
def _initTestingDB():
+ from sqlalchemy import create_engine
from {{package}}.models import initialize_sql
- session = initialize_sql('sqlite://')
+ session = initialize_sql(create_engine('sqlite://'))
return session
class TestMyView(unittest.TestCase):
@@ -21,4 +22,4 @@ class TestMyView(unittest.TestCase):
request = testing.DummyRequest()
info = my_view(request)
self.assertEqual(info['root'].name, 'root')
- self.assertEqual(info['project'], '{{package}}')
+ self.assertEqual(info['project'], '{{project}}')
diff --git a/pyramid/paster_templates/routesalchemy/development.ini_tmpl b/pyramid/paster_templates/routesalchemy/development.ini_tmpl
index de4605e33..a1cbff75f 100644
--- a/pyramid/paster_templates/routesalchemy/development.ini_tmpl
+++ b/pyramid/paster_templates/routesalchemy/development.ini_tmpl
@@ -1,20 +1,53 @@
-[app:{{package}}]
-use = egg:{{package}}
+[app:{{project}}]
+use = egg:{{project}}
reload_templates = true
debug_authorization = false
debug_notfound = false
debug_templates = true
default_locale_name = en
-db_string = sqlite:///%(here)s/{{package}}.db
-db_echo = false
+sqlalchemy.url = sqlite:///%(here)s/{{project}}.db
[pipeline:main]
pipeline =
egg:WebError#evalerror
egg:repoze.tm2#tm
- {{package}}
+ {{project}}
[server:main]
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, sqlalchemy
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/pyramid/paster_templates/starter/+package+/__init__.py_tmpl b/pyramid/paster_templates/starter/+package+/__init__.py_tmpl
index 5f170ad27..85cb7ec6a 100644
--- a/pyramid/paster_templates/starter/+package+/__init__.py_tmpl
+++ b/pyramid/paster_templates/starter/+package+/__init__.py_tmpl
@@ -5,11 +5,9 @@ def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(root_factory=get_root, settings=settings)
- config.begin()
config.add_view('{{package}}.views.my_view',
context='{{package}}.models.MyModel',
renderer='{{package}}:templates/mytemplate.pt')
config.add_static_view('static', '{{package}}:static')
- config.end()
return config.make_wsgi_app()
diff --git a/pyramid/paster_templates/starter/+package+/static/footerbg.png b/pyramid/paster_templates/starter/+package+/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/footerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/static/headerbg.png b/pyramid/paster_templates/starter/+package+/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/headerbg.png
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/static/ie6.css b/pyramid/paster_templates/starter/+package+/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/pyramid/paster_templates/starter/+package+/static/logo.png b/pyramid/paster_templates/starter/+package+/static/logo.png
deleted file mode 100644
index 88f5d9865..000000000
--- a/pyramid/paster_templates/starter/+package+/static/logo.png
+++ /dev/null
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/static/middlebg.png b/pyramid/paster_templates/starter/+package+/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/middlebg.png
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/static/pylons.css b/pyramid/paster_templates/starter/+package+/static/pylons.css
index c153be07f..33b21ac1a 100644
--- a/pyramid/paster_templates/starter/+package+/static/pylons.css
+++ b/pyramid/paster_templates/starter/+package+/static/pylons.css
@@ -4,34 +4,23 @@ body{line-height:1;}
ol,ul{list-style:none;}
blockquote,q{quotes:none;}
blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
-/* remember to define focus styles! */
:focus{outline:0;}
-/* remember to highlight inserts somehow! */
ins{text-decoration:none;}
del{text-decoration:line-through;}
-/* tables still need 'cellspacing="0"' in the markup */
table{border-collapse:collapse;border-spacing:0;}
-/* restyling */
sub{vertical-align:sub;font-size:smaller;line-height:normal;}
sup{vertical-align:super;font-size:smaller;line-height:normal;}
-/* lists */
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;}
-/* nested lists have no top/bottom margins */
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;}
-/* 2 deep unordered lists use a circle */
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;}
-/* 3 deep (or more) unordered lists use a square */
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;/* 28px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h2{font-size:1.5em;/* 24px */
-line-height:1.7em;font-family:helvetica,verdana;}
-h3{font-size:1.25em;/* 20px */
-line-height:1.7em;font-family:helvetica,verdana;}
+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 "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
@@ -42,23 +31,25 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;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;}
-#header{background-color:#e88f00;top:0;font-size:14px;}
-#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
-.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+body h6{font-family:"Neuton","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,#bottom{width:100%;}
-#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
-#bottom{color:#222;background-color:#ffffff;overflow:hidden;padding-bottom:80px;}
-.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
-.top{padding-top:100px;}
+#top{color:#000000;height:230px;
+background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#bottom{color:#222;background-color:#ffffff;}
+.top,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top{padding-top:40px;}
+#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:325px;float:left;padding-right:25px;}
-#right{width:325px;float:right;padding-left:25px;}
+#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;}
@@ -67,7 +58,7 @@ 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=text]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*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/paster_templates/starter/+package+/static/pyramid.png b/pyramid/paster_templates/starter/+package+/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/pyramid.png
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/static/transparent.gif b/pyramid/paster_templates/starter/+package+/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/pyramid/paster_templates/starter/+package+/static/transparent.gif
Binary files differ
diff --git a/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt b/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt
index 6ad23d44f..02fc00eeb 100644
--- a/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt
+++ b/pyramid/paster_templates/starter/+package+/templates/mytemplate.pt
@@ -7,21 +7,21 @@
<meta name="description" content="pyramid web application" />
<link rel="shortcut icon" href="${request.application_url}/static/favicon.ico" />
<link rel="stylesheet" href="${request.application_url}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link href="http://fonts.googleapis.com/css?family=Neuton&amp;subset=latin" rel="stylesheet" type="text/css" media="screen" charset="utf-8" />
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
- <!--[if !IE 7]>
- <style type="text/css">
- #wrap {display:table;height:100%}
- </style>
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.application_url}/static/ie6.css" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
- <div id="header">
- <div class="header">The Pyramid Web Application Development Framework</div>
- </div>
<div id="top">
<div class="top align-center">
- <img src="${request.application_url}/static/logo.png" width="300" height="80"/>
+ <div><img src="${request.application_url}/static/pyramid.png" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
@@ -31,22 +31,19 @@
<div id="bottom">
<div class="bottom">
<div id="left" class="align-right">
- <h3>Search Pyramid documentation</h3>
+ <h2>Search documentation</h2>
<form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
<input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Search" />
+ <input type="submit" id="x" value="Go" />
</form>
</div>
<div id="right" class="align-left">
- <h3>Pyramid links</h3>
+ <h2>Pyramid links</h2>
<ul class="links">
<li>
<a href="http://pylonshq.com">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
- </li>
- <li>
<a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
diff --git a/pyramid/paster_templates/starter/development.ini_tmpl b/pyramid/paster_templates/starter/development.ini_tmpl
index 5031742db..328422ef8 100644
--- a/pyramid/paster_templates/starter/development.ini_tmpl
+++ b/pyramid/paster_templates/starter/development.ini_tmpl
@@ -15,3 +15,29 @@ pipeline =
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl b/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl
index 19f35bee9..3a0c6b7de 100644
--- a/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl
+++ b/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl
@@ -6,8 +6,6 @@ def main(global_config, **settings):
"""
zcml_file = settings.get('configure_zcml', 'configure.zcml')
config = Configurator(root_factory=get_root, settings=settings)
- config.begin()
config.load_zcml(zcml_file)
- config.end()
return config.make_wsgi_app()
diff --git a/pyramid/paster_templates/starter_zcml/development.ini_tmpl b/pyramid/paster_templates/starter_zcml/development.ini_tmpl
index 5031742db..328422ef8 100644
--- a/pyramid/paster_templates/starter_zcml/development.ini_tmpl
+++ b/pyramid/paster_templates/starter_zcml/development.ini_tmpl
@@ -15,3 +15,29 @@ pipeline =
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl b/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl
index 4e879d40f..6b05ac7ad 100644
--- a/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl
+++ b/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl
@@ -14,7 +14,5 @@ def main(global_config, **settings):
def get_root(request):
return finder(request.environ)
config = Configurator(root_factory=get_root, settings=settings)
- config.begin()
config.load_zcml(zcml_file)
- config.end()
return config.make_wsgi_app()
diff --git a/pyramid/paster_templates/zodb/development.ini_tmpl b/pyramid/paster_templates/zodb/development.ini_tmpl
index 85a0681b7..671c8b069 100644
--- a/pyramid/paster_templates/zodb/development.ini_tmpl
+++ b/pyramid/paster_templates/zodb/development.ini_tmpl
@@ -18,3 +18,29 @@ pipeline =
use = egg:Paste#http
host = 0.0.0.0
port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
+
+# End logging configuration
diff --git a/pyramid/path.py b/pyramid/path.py
index 2b557af5f..10647c073 100644
--- a/pyramid/path.py
+++ b/pyramid/path.py
@@ -44,7 +44,8 @@ def package_of(pkg_or_module):
def caller_package(level=2, caller_module=caller_module):
# caller_module in arglist for tests
module = caller_module(level+1)
- if '__init__.py' in getattr(module, '__file__', ''): # empty at >>>
+ f = getattr(module, '__file__', '')
+ if (('__init__.py' in f) or ('__init__$py' in f)): # empty at >>>
# Module is a package
return module
# Go up one level to get package
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index 0c2a7c5db..ba29f80d0 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -4,6 +4,8 @@ import threading
from zope.interface import implements
+from pyramid.interfaces import IChameleonLookup
+from pyramid.interfaces import IChameleonTranslate
from pyramid.interfaces import IRendererGlobalsFactory
from pyramid.interfaces import IRendererFactory
from pyramid.interfaces import IResponseFactory
@@ -150,66 +152,102 @@ def string_renderer_factory(info):
# utility functions, not API
-# Lock to prevent simultaneous registry writes; used in
-# template_renderer_factory. template_renderer_factory may be called
-# at runtime, from more than a single thread.
-registry_lock = threading.Lock()
-
-def template_renderer_factory(info, impl, lock=registry_lock):
- spec = info.name
- reg = info.registry
- package = info.package
-
- isabs = os.path.isabs(spec)
-
- if (not isabs) and (not ':' in spec) and package:
- # relative resource spec
- if not isabs:
- pp = package_path(package)
- spec = os.path.join(pp, spec)
- spec = resource_spec_from_abspath(spec, package)
-
- if os.path.isabs(spec):
- # 'spec' is an absolute filename
- if not os.path.exists(spec):
- raise ValueError('Missing template file: %s' % spec)
- renderer = reg.queryUtility(ITemplateRenderer, name=spec)
- if renderer is None:
- renderer = impl(spec)
- # cache the template
- try:
- lock.acquire()
- reg.registerUtility(renderer, ITemplateRenderer, name=spec)
- finally:
- lock.release()
- else:
- # spec is a package:relpath resource spec
- renderer = reg.queryUtility(ITemplateRenderer, name=spec)
- if renderer is None:
- try:
- package_name, filename = spec.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
- abspath = pkg_resources.resource_filename(package_name, filename)
- if not pkg_resources.resource_exists(package_name, filename):
- raise ValueError(
- 'Missing template resource: %s (%s)' % (spec, abspath))
- renderer = impl(abspath)
- settings = info.settings
- if settings and not settings.get('reload_resources'):
+class ChameleonRendererLookup(object):
+ implements(IChameleonLookup)
+ def __init__(self, impl, registry):
+ self.impl = impl
+ self.registry = registry
+ self.lock = threading.Lock()
+
+ def get_spec(self, name, package):
+ spec = name
+ isabs = os.path.isabs(name)
+
+ if (not isabs) and (not ':' in name) and package:
+ # relative resource spec
+ if not isabs:
+ pp = package_path(package)
+ spec = os.path.join(pp, spec)
+ spec = resource_spec_from_abspath(spec, package)
+ return spec
+
+ @property # wait until completely necessary to look up translator
+ def translate(self):
+ return self.registry.queryUtility(IChameleonTranslate)
+
+ @property # wait until completely necessary to look up debug_templates
+ def debug(self):
+ settings = self.registry.settings or {}
+ return settings.get('debug_templates', False)
+
+ @property # wait until completely necessary to look up reload_templates
+ def auto_reload(self):
+ settings = self.registry.settings or {}
+ return settings.get('reload_templates', False)
+
+ def __call__(self, info):
+ spec = self.get_spec(info.name, info.package)
+ registry = info.registry
+
+ if os.path.isabs(spec):
+ # 'spec' is an absolute filename
+ if not os.path.exists(spec):
+ raise ValueError('Missing template file: %s' % spec)
+ renderer = registry.queryUtility(ITemplateRenderer, name=spec)
+ if renderer is None:
+ renderer = self.impl(spec, self)
# cache the template
try:
- lock.acquire()
- reg.registerUtility(renderer, ITemplateRenderer, name=spec)
+ self.lock.acquire()
+ registry.registerUtility(renderer,
+ ITemplateRenderer, name=spec)
finally:
- lock.release()
-
- return renderer
+ self.lock.release()
+ else:
+ # spec is a package:relpath resource spec
+ renderer = registry.queryUtility(ITemplateRenderer, name=spec)
+ if renderer is None:
+ try:
+ package_name, filename = spec.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
+ abspath = pkg_resources.resource_filename(package_name,
+ filename)
+ if not pkg_resources.resource_exists(package_name, filename):
+ raise ValueError(
+ 'Missing template resource: %s (%s)' % (spec, abspath))
+ renderer = self.impl(abspath, self)
+ settings = info.settings or {}
+ if not settings.get('reload_resources'):
+ # cache the template
+ self.lock.acquire()
+ try:
+ registry.registerUtility(renderer, ITemplateRenderer,
+ name=spec)
+ finally:
+ self.lock.release()
+
+ return renderer
+
+registry_lock = threading.Lock()
-def renderer_from_name(path, package=None): # XXX deprecate?
+def template_renderer_factory(info, impl, lock=registry_lock):
+ registry = info.registry
+ lookup = registry.queryUtility(IChameleonLookup, name=info.type)
+ if lookup is None:
+ lookup = ChameleonRendererLookup(impl, registry)
+ lock.acquire()
+ try:
+ registry.registerUtility(lookup, IChameleonLookup, name=info.type)
+ finally:
+ lock.release()
+ return lookup(info)
+
+# XXX deprecate
+def renderer_from_name(path, package=None):
return RendererHelper(name=path, package=package).renderer
class RendererHelper(object):
diff --git a/pyramid/request.py b/pyramid/request.py
index 891c33fff..2f9ca5819 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -8,8 +8,10 @@ from pyramid.interfaces import ISessionFactory
from pyramid.exceptions import ConfigurationError
from pyramid.decorator import reify
-from pyramid.url import route_url
from pyramid.url import model_url
+from pyramid.url import route_url
+from pyramid.url import static_url
+from pyramid.url import route_path
class TemplateContext(object):
pass
@@ -220,6 +222,75 @@ class Request(WebobRequest):
"""
return model_url(model, self, *elements, **kw)
+ def static_url(self, path, **kw):
+ """ Generates a fully qualified URL for a static :term:`resource`.
+ The resource must live within a location defined via the
+ :meth:`pyramid.configuration.Configurator.add_static_view`
+ :term:`configuration declaration` or the ``<static>`` ZCML
+ directive (see :ref:`static_resources_section`).
+
+ This is a convenience method. The result of calling
+ :meth:`pyramid.request.Request.static_url` is the same as calling
+ :func:`pyramid.url.static_url` with an explicit ``request`` parameter.
+
+ The :meth:`pyramid.request.Request.static_url` method calls the
+ :func:`pyramid.url.static_url` function using the Request object as
+ the ``request`` argument. The ``*kw`` arguments passed to
+ :meth:`pyramid.request.Request.static_url` are passed through to
+ :func:`pyramid.url.static_url` unchanged and its result is returned.
+
+ This call to :meth:`pyramid.request.Request.static_url`::
+
+ request.static_url('mypackage:static/foo.css')
+
+ Is completely equivalent to calling :func:`pyramid.url.static_url`
+ like this::
+
+ from pyramid.url import static_url
+ static_url('mypackage:static/foo.css', request)
+
+ See :func:`pyramid.url.static_url` for more information
+
+ """
+ return static_url(path, self, **kw)
+
+ def route_path(self, route_name, *elements, **kw):
+ """Generates a path (aka a 'relative URL', a URL minus the host,
+ scheme, and port) for a named :app:`Pyramid`
+ :term:`route configuration`.
+
+ This is a convenience method. The result of calling
+ :meth:`pyramid.request.Request.route_path` is the same as calling
+ :func:`pyramid.url.route_path` with an explicit ``request``
+ parameter.
+
+ This method accepts the same arguments as
+ :meth:`pyramid.request.Request.route_url` and performs the same duty.
+ It just omits the host, port, and scheme information in the return
+ value; only the path, query parameters, and anchor data are present
+ in the returned string.
+
+ The :meth:`pyramid.request.Request.route_path` method calls the
+ :func:`pyramid.url.route_path` function using the Request object as
+ the ``request`` argument. The ``*elements`` and ``*kw`` arguments
+ passed to :meth:`pyramid.request.Request.route_path` are passed
+ through to :func:`pyramid.url.route_path` unchanged and its result is
+ returned.
+
+ This call to :meth:`pyramid.request.Request.route_path`::
+
+ request.route_path('foobar')
+
+ Is completely equivalent to calling :func:`pyramid.url.route_path`
+ like this::
+
+ from pyramid.url import route_path
+ route_path('foobar', request)
+
+ See :func:`pyramid.url.route_path` for more information
+ """
+ return route_path(route_name, self, *elements, **kw)
+
# override default WebOb "environ['adhoc_attr']" mutation behavior
__getattr__ = object.__getattribute__
__setattr__ = object.__setattr__
diff --git a/pyramid/settings.py b/pyramid/settings.py
index 96ad3336a..928dd1915 100644
--- a/pyramid/settings.py
+++ b/pyramid/settings.py
@@ -1,5 +1,6 @@
import os
+from zope.deprecation import deprecated
from zope.interface import implements
from pyramid.interfaces import ISettings
@@ -75,10 +76,22 @@ def get_settings():
.. note:: the
:class:`pyramid.configuration.Configurator.get_settings` method
performs the same duty.
+
+ .. warning:: This method is deprecated as of Pyramid 1.0. Use
+ ``pyramid.threadlocals.get_current_registry().settings`` instead or use '
+ the ``settings`` attribute of the registry available from the request
+ (``request.registry.settings``).
"""
reg = get_current_registry()
return reg.settings
+deprecated(
+ 'get_settings',
+ '(pyramid.settings.get_settings is deprecated as of Pyramid 1.0. Use'
+ '``pyramid.threadlocal.get_current_registry().settings`` instead or use '
+ 'the ``settings`` attribute of the registry available from the request '
+ '(``request.registry.settings``)).')
+
def asbool(s):
""" Return the boolean value ``True`` if the case-lowered value of string
input ``s`` is any of ``t``, ``true``, ``y``, ``on``, or ``1``, otherwise
diff --git a/pyramid/testing.py b/pyramid/testing.py
index e8e843bff..cc0ee5c5b 100644
--- a/pyramid/testing.py
+++ b/pyramid/testing.py
@@ -1,4 +1,5 @@
import copy
+import os
from zope.configuration.xmlconfig import _clearContext
@@ -20,9 +21,6 @@ from pyramid.security import Everyone
from pyramid.security import has_permission
from pyramid.threadlocal import get_current_registry
from pyramid.threadlocal import manager
-from pyramid.zcml import zcml_configure # API
-
-zcml_configure # prevent pyflakes from complaining
_marker = object()
@@ -43,7 +41,7 @@ def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True):
argument. The authentication policy will return the userid
identifier implied by the ``userid`` argument and the group ids
implied by the ``groupids`` argument when the
- :func:`pyramid.security.authenticated_userid` or
+ :func:`pyramid.security.authenticated_userid` or
:func:`pyramid.security.effective_principals` APIs are used.
This function is most useful when testing code that uses the APIs
@@ -121,7 +119,7 @@ def registerTemplateRenderer(path, renderer=None):
.. warning:: This API is deprecated as of :app:`Pyramid` 1.0.
Instead use the
- :meth:`pyramid.configuration.Configurator.testing_add_template``
+ :meth:`pyramid.configuration.Configurator.testing_add_template`
method in your unit and integration tests.
"""
@@ -153,7 +151,7 @@ def registerView(name, result='', view=None, for_=(Interface, Interface),
.. warning:: This API is deprecated as of :app:`Pyramid` 1.0.
Instead use the
- :meth:`pyramid.configuration.Configurator.add_view``
+ :meth:`pyramid.configuration.Configurator.add_view`
method in your unit and integration tests.
"""
for_ = (IViewClassifier, ) + for_
@@ -213,7 +211,7 @@ def registerAdapter(impl, for_=Interface, provides=Interface, name=''):
The ``name`` argument is the empty string by default; it implies
the name under which the adapter is registered.
-
+
See `The ZCA book <http://www.muthukadan.net/docs/zca.html>`_ for
more information about ZCA adapters.
@@ -277,19 +275,19 @@ def registerRoute(pattern, name, factory=None):
def registerSettings(dictarg=None, **kw):
"""Register one or more 'setting' key/value pairs. A setting is
a single key/value pair in the dictionary-ish object returned from
- the API :func:`pyramid.settings.get_settings`.
+ the API :attr:`pyramid.registry.Registry.settings`.
You may pass a dictionary::
registerSettings({'external_uri':'http://example.com'})
Or a set of key/value pairs::
-
+
registerSettings(external_uri='http://example.com')
- Use of this function is required when you need to test code that
- calls the :func:`pyramid.settings.get_settings` API and which
- uses return values from that API.
+ Use of this function is required when you need to test code that calls
+ the :attr:`pyramid.registry.Registry.settings` API and which uses return
+ values from that API.
.. warning:: This API is deprecated as of :app:`Pyramid` 1.0.
Instead use the
@@ -363,7 +361,7 @@ class DummyTemplateRenderer(object):
def implementation(self):
return self._implementation
-
+
def __call__(self, kw, system=None):
if system:
self._received.update(system)
@@ -395,7 +393,7 @@ class DummyTemplateRenderer(object):
raise AssertionError(
'A value for key "%s" was not passed to the renderer'
% k)
-
+
if myval != v:
raise AssertionError(
'\nasserted value for %s: %r\nactual value: %r' % (
@@ -433,7 +431,7 @@ class DummyModel:
val.__name__ = name
val.__parent__ = self
self.subs[name] = val
-
+
def __getitem__(self, name):
""" Return a named subobject (see ``__setitem__``)"""
ob = self.subs[name]
@@ -467,7 +465,7 @@ class DummyModel:
def __contains__(self, name):
return name in self.subs
-
+
def clone(self, __name__=_marker, __parent__=_marker, **kw):
""" Create a clone of the model object. If ``__name__`` or
``__parent__`` arguments are passed, use these values to
@@ -487,7 +485,7 @@ class DummyModel:
class DummyRequest(object):
""" A dummy request object (imitates a :term:`request` object).
-
+
The ``params``, ``environ``, ``headers``, ``path``, and
``cookies`` arguments correspond to their :term`WebOb`
equivalents.
@@ -505,6 +503,7 @@ class DummyRequest(object):
application_url = 'http://example.com'
host = 'example.com:80'
content_length = 0
+ query_string = ''
response_callbacks = ()
def __init__(self, params=None, environ=None, headers=None, path='/',
cookies=None, post=None, **kw):
@@ -723,8 +722,8 @@ class DummyRendererFactory(object):
raise KeyError('No testing renderer registered for %r' %
spec)
return renderer
-
-
+
+
class MockTemplate(object):
def __init__(self, response):
self._received = {}
@@ -736,3 +735,23 @@ class MockTemplate(object):
def __call__(self, *arg, **kw):
self._received.update(kw)
return self.response
+
+def skip_on(*platforms):
+ def decorator(func):
+ def wrapper(*args, **kw):
+ for platform in platforms:
+ if skip_on.os_name.startswith(platform):
+ return
+ if platform == 'pypy' and skip_on.pypy: # pragma: no cover
+ return
+ return func(*args, **kw)
+ wrapper.__name__ = func.__name__
+ wrapper.__doc__ = func.__doc__
+ return wrapper
+ return decorator
+skip_on.os_name = os.name # for testing
+try: # pragma: no cover
+ import __pypy__
+ skip_on.pypy = True
+except ImportError:
+ skip_on.pypy = False
diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py
index 8bae18fba..d9d0c2c97 100644
--- a/pyramid/tests/test_authentication.py
+++ b/pyramid/tests/test_authentication.py
@@ -411,8 +411,10 @@ class TestAuthTktCookieHelper(unittest.TestCase):
def test_identify_cookie_reissue(self):
import time
- plugin = self._makeOne('secret', timeout=5, reissue_time=0)
- plugin.auth_tkt.timestamp = time.time()
+ plugin = self._makeOne('secret', timeout=10, reissue_time=0)
+ now = time.time()
+ plugin.auth_tkt.timestamp = now
+ plugin.now = now + 1
request = self._makeRequest({'HTTP_COOKIE':'auth_tkt=bogus'})
result = plugin.identify(request)
self.failUnless(result)
diff --git a/pyramid/tests/test_chameleon_text.py b/pyramid/tests/test_chameleon_text.py
index 764124502..9486bbc88 100644
--- a/pyramid/tests/test_chameleon_text.py
+++ b/pyramid/tests/test_chameleon_text.py
@@ -1,6 +1,7 @@
import unittest
from pyramid.testing import cleanUp
+from pyramid.testing import skip_on
class Base:
def setUp(self):
@@ -11,9 +12,13 @@ class Base:
os.unlink(self._getTemplatePath('minimal.txt.py'))
except:
pass
+ from zope.deprecation import __show__
+ __show__.off()
def tearDown(self):
cleanUp()
+ from zope.deprecation import __show__
+ __show__.on()
def _getTemplatePath(self, name):
import os
@@ -46,82 +51,98 @@ class TextTemplateRendererTests(Base, unittest.TestCase):
klass = self._getTargetClass()
return klass(*arg, **kw)
+ @skip_on('pypy')
def test_instance_implements_ITemplate(self):
from zope.interface.verify import verifyObject
from pyramid.interfaces import ITemplateRenderer
path = self._getTemplatePath('minimal.txt')
- verifyObject(ITemplateRenderer, self._makeOne(path))
+ lookup = DummyLookup()
+ verifyObject(ITemplateRenderer, self._makeOne(path, lookup))
+ @skip_on('pypy')
def test_class_implements_ITemplate(self):
from zope.interface.verify import verifyClass
from pyramid.interfaces import ITemplateRenderer
verifyClass(ITemplateRenderer, self._getTargetClass())
+ @skip_on('java', 'pypy')
def test_template_reified(self):
minimal = self._getTemplatePath('minimal.txt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
self.failIf('template' in instance.__dict__)
template = instance.template
self.assertEqual(template, instance.__dict__['template'])
+ @skip_on('java', 'pypy')
def test_template_with_ichameleon_translate(self):
- from pyramid.interfaces import IChameleonTranslate
- def ct(): pass
- self.config.registry.registerUtility(ct, IChameleonTranslate)
minimal = self._getTemplatePath('minimal.txt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
self.failIf('template' in instance.__dict__)
template = instance.template
- self.assertEqual(template.translate, ct)
+ self.assertEqual(template.translate, lookup.translate)
+ @skip_on('java', 'pypy')
def test_template_with_debug_templates(self):
- self.config.add_settings({'debug_templates':True})
minimal = self._getTemplatePath('minimal.txt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ lookup.debug = True
+ instance = self._makeOne(minimal, lookup)
self.failIf('template' in instance.__dict__)
template = instance.template
self.assertEqual(template.debug, True)
+ @skip_on('java', 'pypy')
def test_template_with_reload_templates(self):
- self.config.add_settings({'reload_templates':True})
minimal = self._getTemplatePath('minimal.txt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ lookup.auto_reload = True
+ instance = self._makeOne(minimal, lookup)
self.failIf('template' in instance.__dict__)
template = instance.template
self.assertEqual(template.auto_reload, True)
- def test_template_with_emptydict(self):
- from pyramid.interfaces import ISettings
- self.config.registry.registerUtility({}, ISettings)
+ @skip_on('java', 'pypy')
+ def test_template_without_reload_templates(self):
minimal = self._getTemplatePath('minimal.txt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ lookup.auto_reload = False
+ instance = self._makeOne(minimal, lookup)
self.failIf('template' in instance.__dict__)
template = instance.template
self.assertEqual(template.auto_reload, False)
- self.assertEqual(template.debug, False)
+ @skip_on('java', 'pypy')
def test_call(self):
minimal = self._getTemplatePath('minimal.txt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
result = instance({}, {})
self.failUnless(isinstance(result, str))
self.assertEqual(result, 'Hello.\n')
+ @skip_on('java', 'pypy')
def test_call_with_nondict_value(self):
minimal = self._getTemplatePath('minimal.txt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
self.assertRaises(ValueError, instance, None, {})
+ @skip_on('java', 'pypy')
def test_call_nonminimal(self):
nonminimal = self._getTemplatePath('nonminimal.txt')
- instance = self._makeOne(nonminimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(nonminimal, lookup)
result = instance({'name':'Chris'}, {})
self.failUnless(isinstance(result, str))
self.assertEqual(result, 'Hello, Chris!\n')
+ @skip_on('java', 'pypy')
def test_implementation(self):
minimal = self._getTemplatePath('minimal.txt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
result = instance.implementation()()
self.failUnless(isinstance(result, str))
self.assertEqual(result, 'Hello.\n')
@@ -131,6 +152,7 @@ class RenderTemplateTests(Base, unittest.TestCase):
from pyramid.chameleon_text import render_template
return render_template(name, **kw)
+ @skip_on('java', 'pypy')
def test_it(self):
minimal = self._getTemplatePath('minimal.txt')
result = self._callFUT(minimal)
@@ -142,6 +164,7 @@ class RenderTemplateToResponseTests(Base, unittest.TestCase):
from pyramid.chameleon_text import render_template_to_response
return render_template_to_response(name, **kw)
+ @skip_on('java', 'pypy')
def test_minimal(self):
minimal = self._getTemplatePath('minimal.txt')
result = self._callFUT(minimal)
@@ -151,6 +174,7 @@ class RenderTemplateToResponseTests(Base, unittest.TestCase):
self.assertEqual(result.status, '200 OK')
self.assertEqual(len(result.headerlist), 2)
+ @skip_on('java', 'pypy')
def test_iresponsefactory_override(self):
from webob import Response
class Response2(Response):
@@ -166,6 +190,7 @@ class GetRendererTests(Base, unittest.TestCase):
from pyramid.chameleon_text import get_renderer
return get_renderer(name)
+ @skip_on('java', 'pypy')
def test_it(self):
from pyramid.interfaces import IRendererFactory
class Dummy:
@@ -183,6 +208,7 @@ class GetTemplateTests(Base, unittest.TestCase):
from pyramid.chameleon_text import get_template
return get_template(name)
+ @skip_on('java', 'pypy')
def test_it(self):
from pyramid.interfaces import IRendererFactory
class Dummy:
@@ -196,3 +222,8 @@ class GetTemplateTests(Base, unittest.TestCase):
result = self._callFUT('foo')
self.failUnless(result is renderer.template)
+class DummyLookup(object):
+ auto_reload=True
+ debug = True
+ def translate(self, msg): pass
+
diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py
index a969bac00..802f4460f 100644
--- a/pyramid/tests/test_chameleon_zpt.py
+++ b/pyramid/tests/test_chameleon_zpt.py
@@ -1,13 +1,18 @@
import unittest
from pyramid.testing import cleanUp
+from pyramid.testing import skip_on
class Base(object):
def setUp(self):
cleanUp()
+ from zope.deprecation import __show__
+ __show__.off()
def tearDown(self):
cleanUp()
+ from zope.deprecation import __show__
+ __show__.on()
def _getTemplatePath(self, name):
import os
@@ -39,76 +44,100 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase):
klass = self._getTargetClass()
return klass(*arg, **kw)
+ @skip_on('pypy')
def test_instance_implements_ITemplate(self):
from zope.interface.verify import verifyObject
from pyramid.interfaces import ITemplateRenderer
path = self._getTemplatePath('minimal.pt')
- verifyObject(ITemplateRenderer, self._makeOne(path))
+ lookup = DummyLookup()
+ verifyObject(ITemplateRenderer, self._makeOne(path, lookup))
+ @skip_on('pypy')
def test_class_implements_ITemplate(self):
from zope.interface.verify import verifyClass
from pyramid.interfaces import ITemplateRenderer
verifyClass(ITemplateRenderer, self._getTargetClass())
+ @skip_on('java', 'pypy')
def test_call(self):
minimal = self._getTemplatePath('minimal.pt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
result = instance({}, {})
self.failUnless(isinstance(result, unicode))
self.assertEqual(result,
'<div xmlns="http://www.w3.org/1999/xhtml">\n</div>')
+ @skip_on('java', 'pypy')
def test_template_reified(self):
minimal = self._getTemplatePath('minimal.pt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
self.failIf('template' in instance.__dict__)
template = instance.template
self.assertEqual(template, instance.__dict__['template'])
+ @skip_on('java', 'pypy')
def test_template_with_ichameleon_translate(self):
- from pyramid.interfaces import IChameleonTranslate
- def ct(): pass
- self.config.registry.registerUtility(ct, IChameleonTranslate)
minimal = self._getTemplatePath('minimal.pt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
self.failIf('template' in instance.__dict__)
template = instance.template
- self.assertEqual(template.translate, ct)
+ self.assertEqual(template.translate, lookup.translate)
+ @skip_on('java', 'pypy')
def test_template_with_debug_templates(self):
- self.config.add_settings({'debug_templates':True})
minimal = self._getTemplatePath('minimal.pt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ lookup.debug = True
+ instance = self._makeOne(minimal, lookup)
self.failIf('template' in instance.__dict__)
template = instance.template
self.assertEqual(template.debug, True)
+ @skip_on('java', 'pypy')
+ def test_template_without_debug_templates(self):
+ minimal = self._getTemplatePath('minimal.pt')
+ lookup = DummyLookup()
+ lookup.debug = False
+ instance = self._makeOne(minimal, lookup)
+ self.failIf('template' in instance.__dict__)
+ template = instance.template
+ self.assertEqual(template.debug, False)
+
+ @skip_on('java', 'pypy')
def test_template_with_reload_templates(self):
- self.config.add_settings({'reload_templates':True})
minimal = self._getTemplatePath('minimal.pt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ lookup.auto_reload = True
+ instance = self._makeOne(minimal, lookup)
self.failIf('template' in instance.__dict__)
template = instance.template
self.assertEqual(template.auto_reload, True)
- def test_template_with_emptydict(self):
- from pyramid.interfaces import ISettings
- self.config.registry.registerUtility({}, ISettings)
+ @skip_on('java', 'pypy')
+ def test_template_without_reload_templates(self):
minimal = self._getTemplatePath('minimal.pt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ lookup.auto_reload = False
+ instance = self._makeOne(minimal, lookup)
self.failIf('template' in instance.__dict__)
template = instance.template
self.assertEqual(template.auto_reload, False)
- self.assertEqual(template.debug, False)
+ @skip_on('java', 'pypy')
def test_call_with_nondict_value(self):
minimal = self._getTemplatePath('minimal.pt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
self.assertRaises(ValueError, instance, None, {})
+ @skip_on('java', 'pypy')
def test_implementation(self):
minimal = self._getTemplatePath('minimal.pt')
- instance = self._makeOne(minimal)
+ lookup = DummyLookup()
+ instance = self._makeOne(minimal, lookup)
result = instance.implementation()()
self.failUnless(isinstance(result, unicode))
self.assertEqual(result,
@@ -120,6 +149,7 @@ class RenderTemplateTests(Base, unittest.TestCase):
from pyramid.chameleon_zpt import render_template
return render_template(name, **kw)
+ @skip_on('java', 'pypy')
def test_it(self):
minimal = self._getTemplatePath('minimal.pt')
result = self._callFUT(minimal)
@@ -128,16 +158,11 @@ class RenderTemplateTests(Base, unittest.TestCase):
'<div xmlns="http://www.w3.org/1999/xhtml">\n</div>')
class RenderTemplateToResponseTests(Base, unittest.TestCase):
- def setUp(self):
- cleanUp()
-
- def tearDown(self):
- cleanUp()
-
def _callFUT(self, name, **kw):
from pyramid.chameleon_zpt import render_template_to_response
return render_template_to_response(name, **kw)
+ @skip_on('java', 'pypy')
def test_it(self):
minimal = self._getTemplatePath('minimal.pt')
result = self._callFUT(minimal)
@@ -148,6 +173,7 @@ class RenderTemplateToResponseTests(Base, unittest.TestCase):
self.assertEqual(result.status, '200 OK')
self.assertEqual(len(result.headerlist), 2)
+ @skip_on('java', 'pypy')
def test_iresponsefactory_override(self):
from webob import Response
class Response2(Response):
@@ -163,6 +189,7 @@ class GetRendererTests(Base, unittest.TestCase):
from pyramid.chameleon_zpt import get_renderer
return get_renderer(name)
+ @skip_on('java', 'pypy')
def test_it(self):
from pyramid.interfaces import IRendererFactory
class Dummy:
@@ -180,6 +207,7 @@ class GetTemplateTests(Base, unittest.TestCase):
from pyramid.chameleon_zpt import get_template
return get_template(name)
+ @skip_on('java', 'pypy')
def test_it(self):
from pyramid.interfaces import IRendererFactory
class Dummy:
@@ -192,3 +220,8 @@ class GetTemplateTests(Base, unittest.TestCase):
self._registerUtility(rf, IRendererFactory, name='foo')
result = self._callFUT('foo')
self.failUnless(result is renderer.template)
+
+class DummyLookup(object):
+ auto_reload=True
+ debug = True
+ def translate(self, msg): pass
diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py
index ded17cb33..938db3c31 100644
--- a/pyramid/tests/test_configuration.py
+++ b/pyramid/tests/test_configuration.py
@@ -2,6 +2,11 @@ import unittest
from pyramid import testing
+try:
+ import __pypy__
+except:
+ __pypy__ = None
+
class ConfiguratorTests(unittest.TestCase):
def _makeOne(self, *arg, **kw):
from pyramid.configuration import Configurator
@@ -90,8 +95,9 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(config.package, this_pkg)
self.failUnless(config.registry.getUtility(IRendererFactory, 'json'))
self.failUnless(config.registry.getUtility(IRendererFactory, 'string'))
- self.failUnless(config.registry.getUtility(IRendererFactory, '.pt'))
- self.failUnless(config.registry.getUtility(IRendererFactory, '.txt'))
+ if not __pypy__:
+ self.failUnless(config.registry.getUtility(IRendererFactory, '.pt'))
+ self.failUnless(config.registry.getUtility(IRendererFactory,'.txt'))
self.failUnless(config.registry.getUtility(IRendererFactory, '.mak'))
self.failUnless(config.registry.getUtility(IRendererFactory, '.mako'))
@@ -210,9 +216,8 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(result, pyramid.tests)
def test_maybe_dotted_string_fail(self):
- from pyramid.configuration import ConfigurationError
config = self._makeOne()
- self.assertRaises(ConfigurationError,
+ self.assertRaises(ImportError,
config.maybe_dotted, 'cant.be.found')
def test_maybe_dotted_notstring_success(self):
@@ -2553,6 +2558,36 @@ class ConfiguratorTests(unittest.TestCase):
self.failIf(result is view)
self.assertEqual(result(None, None).body, 'moo')
+ def test_derive_view_with_default_renderer_no_explicit_renderer(self):
+ def view(request):
+ return 'OK'
+ config = self._makeOne()
+ class moo(object):
+ def __init__(self, *arg, **kw):
+ pass
+ def __call__(self, *arg, **kw):
+ return 'moo'
+ config.add_renderer(None, moo)
+ result = config.derive_view(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None).body, 'moo')
+
+ def test_derive_view_with_default_renderer_with_explicit_renderer(self):
+ def view(request):
+ return 'OK'
+ config = self._makeOne()
+ class moo(object): pass
+ class foo(object):
+ def __init__(self, *arg, **kw):
+ pass
+ def __call__(self, *arg, **kw):
+ return 'foo'
+ config.add_renderer(None, moo)
+ config.add_renderer('foo', foo)
+ result = config.derive_view(view, renderer='foo')
+ self.failIf(result is view)
+ self.assertEqual(result(None, None).body, 'foo')
+
def test_derive_view_class_without_attr(self):
class View(object):
def __init__(self, request):
@@ -2940,6 +2975,7 @@ class ConfiguratorTests(unittest.TestCase):
pyramid.tests)
def test_scan_integration(self):
+ import os
from zope.interface import alsoProvides
from pyramid.interfaces import IRequest
from pyramid.view import render_view_to_response
@@ -3011,8 +3047,12 @@ class ConfiguratorTests(unittest.TestCase):
result = render_view_to_response(ctx, req, 'another_stacked_class2')
self.assertEqual(result, 'another_stacked_class')
- self.assertRaises(TypeError,
- render_view_to_response, ctx, req, 'basemethod')
+ if not os.name.startswith('java'):
+ # on Jython, a class without an __init__ apparently accepts
+ # any number of arguments without raising a TypeError.
+
+ self.assertRaises(TypeError,
+ render_view_to_response, ctx, req, 'basemethod')
result = render_view_to_response(ctx, req, 'method1')
self.assertEqual(result, 'method1')
@@ -3231,9 +3271,9 @@ class Test__map_view(unittest.TestCase):
request.registry = self.registry
return request
- def _callFUT(self, *arg, **kw):
+ def _callFUT(self, view, **kw):
from pyramid.configuration import _map_view
- return _map_view(*arg, **kw)
+ return _map_view(view, self.registry, **kw)
def test__map_view_as_function_context_and_request(self):
def view(context, request):
@@ -3532,8 +3572,7 @@ class Test__map_view(unittest.TestCase):
def view(context, request):
return {'a':'1'}
info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, renderer=info,
- registry=self.registry)
+ result = self._callFUT(view, renderer=info)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
@@ -4247,183 +4286,6 @@ class TestMakeApp(unittest.TestCase):
Configurator=DummyConfigurator)
self.assertEqual(app.zcml_file, '2.zcml')
-class TestDottedNameResolver(unittest.TestCase):
- def _makeOne(self, package=None):
- from pyramid.configuration import DottedNameResolver
- return DottedNameResolver(package)
-
- def config_exc(self, func, *arg, **kw):
- from pyramid.exceptions import ConfigurationError
- try:
- func(*arg, **kw)
- except ConfigurationError, e:
- return e
- else:
- raise AssertionError('Invalid not raised') # pragma: no cover
-
- def test_zope_dottedname_style_resolve_absolute(self):
- typ = self._makeOne()
- result = typ._zope_dottedname_style(
- 'pyramid.tests.test_configuration.TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test_zope_dottedname_style_irrresolveable_absolute(self):
- typ = self._makeOne()
- self.assertRaises(ImportError, typ._zope_dottedname_style,
- 'pyramid.test_configuration.nonexisting_name')
-
- def test__zope_dottedname_style_resolve_relative(self):
- import pyramid.tests
- typ = self._makeOne(package=pyramid.tests)
- result = typ._zope_dottedname_style(
- '.test_configuration.TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__zope_dottedname_style_resolve_relative_leading_dots(self):
- import pyramid.tests.test_configuration
- typ = self._makeOne(package=pyramid.tests)
- result = typ._zope_dottedname_style(
- '..tests.test_configuration.TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__zope_dottedname_style_resolve_relative_is_dot(self):
- import pyramid.tests
- typ = self._makeOne(package=pyramid.tests)
- result = typ._zope_dottedname_style('.')
- self.assertEqual(result, pyramid.tests)
-
- def test__zope_dottedname_style_irresolveable_relative_is_dot(self):
- typ = self._makeOne()
- e = self.config_exc(typ._zope_dottedname_style, '.')
- self.assertEqual(
- e.args[0],
- "relative name '.' irresolveable without package")
-
- def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self):
- typ = self._makeOne()
- e = self.config_exc(typ._zope_dottedname_style, '.whatever')
- self.assertEqual(
- e.args[0],
- "relative name '.whatever' irresolveable without package")
-
- def test_zope_dottedname_style_irrresolveable_relative(self):
- import pyramid.tests
- typ = self._makeOne(package=pyramid.tests)
- self.assertRaises(ImportError, typ._zope_dottedname_style,
- '.notexisting')
-
- def test__zope_dottedname_style_resolveable_relative(self):
- import pyramid
- typ = self._makeOne(package=pyramid)
- result = typ._zope_dottedname_style('.tests')
- from pyramid import tests
- self.assertEqual(result, tests)
-
- def test__zope_dottedname_style_irresolveable_absolute(self):
- typ = self._makeOne()
- self.assertRaises(
- ImportError,
- typ._zope_dottedname_style, 'pyramid.fudge.bar')
-
- def test__zope_dottedname_style_resolveable_absolute(self):
- typ = self._makeOne()
- result = typ._zope_dottedname_style(
- 'pyramid.tests.test_configuration.TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__pkg_resources_style_resolve_absolute(self):
- typ = self._makeOne()
- result = typ._pkg_resources_style(
- 'pyramid.tests.test_configuration:TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__pkg_resources_style_irrresolveable_absolute(self):
- typ = self._makeOne()
- self.assertRaises(ImportError, typ._pkg_resources_style,
- 'pyramid.tests:nonexisting')
-
- def test__pkg_resources_style_resolve_relative(self):
- import pyramid.tests
- typ = self._makeOne(package=pyramid.tests)
- result = typ._pkg_resources_style(
- '.test_configuration:TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test__pkg_resources_style_resolve_relative_is_dot(self):
- import pyramid.tests
- typ = self._makeOne(package=pyramid.tests)
- result = typ._pkg_resources_style('.')
- self.assertEqual(result, pyramid.tests)
-
- def test__pkg_resources_style_resolve_relative_nocurrentpackage(self):
- typ = self._makeOne()
- from pyramid.exceptions import ConfigurationError
- self.assertRaises(ConfigurationError, typ._pkg_resources_style,
- '.whatever')
-
- def test__pkg_resources_style_irrresolveable_relative(self):
- import pyramid
- typ = self._makeOne(package=pyramid)
- self.assertRaises(ImportError, typ._pkg_resources_style,
- ':notexisting')
-
- def test_resolve_not_a_string(self):
- typ = self._makeOne()
- e = self.config_exc(typ.resolve, None)
- self.assertEqual(e.args[0], 'None is not a string')
-
- def test_resolve_using_pkgresources_style(self):
- typ = self._makeOne()
- result = typ.resolve(
- 'pyramid.tests.test_configuration:TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test_resolve_using_zope_dottedname_style(self):
- typ = self._makeOne()
- result = typ.resolve(
- 'pyramid.tests.test_configuration:TestDottedNameResolver')
- self.assertEqual(result, self.__class__)
-
- def test_resolve_missing_raises(self):
- typ = self._makeOne()
- e = self.config_exc(typ.resolve, 'cant.be.found')
- self.assertEqual(e.args[0],
- "The dotted name 'cant.be.found' cannot be imported")
-
- def test_ctor_string_module_resolveable(self):
- import pyramid.tests
- typ = self._makeOne('pyramid.tests.test_configuration')
- self.assertEqual(typ.package, pyramid.tests)
- self.assertEqual(typ.package_name, 'pyramid.tests')
-
- def test_ctor_string_package_resolveable(self):
- import pyramid.tests
- typ = self._makeOne('pyramid.tests')
- self.assertEqual(typ.package, pyramid.tests)
- self.assertEqual(typ.package_name, 'pyramid.tests')
-
- def test_ctor_string_irresolveable(self):
- from pyramid.configuration import ConfigurationError
- self.assertRaises(ConfigurationError, self._makeOne, 'cant.be.found')
-
- def test_ctor_module(self):
- import pyramid.tests
- import pyramid.tests.test_configuration
- typ = self._makeOne(pyramid.tests.test_configuration)
- self.assertEqual(typ.package, pyramid.tests)
- self.assertEqual(typ.package_name, 'pyramid.tests')
-
- def test_ctor_package(self):
- import pyramid.tests
- typ = self._makeOne(pyramid.tests)
- self.assertEqual(typ.package, pyramid.tests)
- self.assertEqual(typ.package_name, 'pyramid.tests')
-
- def test_ctor_None(self):
- typ = self._makeOne(None)
- self.assertEqual(typ.package, None)
- self.assertEqual(typ.package_name, None)
-
class Test_isexception(unittest.TestCase):
def _callFUT(self, ob):
from pyramid.configuration import isexception
diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py
index e8d119e79..c70985e79 100644
--- a/pyramid/tests/test_integration.py
+++ b/pyramid/tests/test_integration.py
@@ -7,8 +7,6 @@ from pyramid.view import static
from zope.interface import Interface
-from pyramid import testing
-
class INothing(Interface):
pass
@@ -65,192 +63,181 @@ class TestStaticApp(unittest.TestCase):
result.body,
open(os.path.join(here, 'fixtures/minimal.pt'), 'r').read())
-class TwillBase(unittest.TestCase):
+class IntegrationBase(unittest.TestCase):
root_factory = None
def setUp(self):
- import sys
- import twill
from pyramid.configuration import Configurator
config = Configurator(root_factory=self.root_factory)
+ config.begin()
config.load_zcml(self.config)
- twill.add_wsgi_intercept('localhost', 6543, config.make_wsgi_app)
- if sys.platform is 'win32': # pragma: no cover
- out = open('nul:', 'wb')
- else:
- out = open('/dev/null', 'wb')
- twill.set_output(out)
- testing.setUp(registry=config.registry)
+ app = config.make_wsgi_app()
+ from webtest import TestApp
+ self.testapp = TestApp(app)
+ self.config = config
def tearDown(self):
- import twill
- import twill.commands
- twill.commands.reset_browser()
- twill.remove_wsgi_intercept('localhost', 6543)
- twill.set_output(None)
- testing.tearDown()
-
-class TestFixtureApp(TwillBase):
+ self.config.end()
+
+class TestFixtureApp(IntegrationBase):
config = 'pyramid.tests.fixtureapp:configure.zcml'
- def test_it(self):
- import twill.commands
- browser = twill.commands.get_browser()
- browser.go('http://localhost:6543/another.html')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'fixture')
- browser.go('http://localhost:6543')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'fixture')
- browser.go('http://localhost:6543/dummyskin.html')
- self.assertEqual(browser.get_code(), 404)
- browser.go('http://localhost:6543/error.html')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'supressed')
- browser.go('http://localhost:6543/protected.html')
- self.assertEqual(browser.get_code(), 401)
-
-class TestCCBug(TwillBase):
+ def test_another(self):
+ res = self.testapp.get('/another.html', status=200)
+ self.assertEqual(res.body, 'fixture')
+
+ def test_root(self):
+ res = self.testapp.get('/', status=200)
+ self.assertEqual(res.body, 'fixture')
+
+ def test_dummyskin(self):
+ self.testapp.get('/dummyskin.html', status=404)
+
+ def test_error(self):
+ res = self.testapp.get('/error.html', status=200)
+ self.assertEqual(res.body, 'supressed')
+
+ def test_protected(self):
+ self.testapp.get('/protected.html', status=401)
+
+class TestCCBug(IntegrationBase):
# "unordered" as reported in IRC by author of
# http://labs.creativecommons.org/2010/01/13/cc-engine-and-web-non-frameworks/
config = 'pyramid.tests.ccbugapp:configure.zcml'
- def test_it(self):
- import twill.commands
- browser = twill.commands.get_browser()
- browser.go('http://localhost:6543/licenses/1/v1/rdf')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'rdf')
- browser.go('http://localhost:6543/licenses/1/v1/juri')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'juri')
-
-class TestHybridApp(TwillBase):
+ def test_rdf(self):
+ res = self.testapp.get('/licenses/1/v1/rdf', status=200)
+ self.assertEqual(res.body, 'rdf')
+
+ def test_juri(self):
+ res = self.testapp.get('/licenses/1/v1/juri', status=200)
+ self.assertEqual(res.body, 'juri')
+
+class TestHybridApp(IntegrationBase):
# make sure views registered for a route "win" over views registered
# without one, even though the context of the non-route view may
# be more specific than the route view.
config = 'pyramid.tests.hybridapp:configure.zcml'
- def test_it(self):
- import twill.commands
- browser = twill.commands.get_browser()
- browser.go('http://localhost:6543/')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'global')
- browser.go('http://localhost:6543/abc')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'route')
- browser.go('http://localhost:6543/def')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'route2')
- browser.go('http://localhost:6543/ghi')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'global')
- browser.go('http://localhost:6543/jkl')
- self.assertEqual(browser.get_code(), 404)
- browser.go('http://localhost:6543/mno/global2')
- self.assertEqual(browser.get_code(), 404)
- browser.go('http://localhost:6543/pqr/global2')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'global2')
- browser.go('http://localhost:6543/error')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'supressed')
- browser.go('http://localhost:6543/error2')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'supressed2')
- browser.go('http://localhost:6543/error_sub')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'supressed2')
-
-class TestRestBugApp(TwillBase):
+ def test_root(self):
+ res = self.testapp.get('/', status=200)
+ self.assertEqual(res.body, 'global')
+
+ def test_abc(self):
+ res = self.testapp.get('/abc', status=200)
+ self.assertEqual(res.body, 'route')
+
+ def test_def(self):
+ res = self.testapp.get('/def', status=200)
+ self.assertEqual(res.body, 'route2')
+
+ def test_ghi(self):
+ res = self.testapp.get('/ghi', status=200)
+ self.assertEqual(res.body, 'global')
+
+ def test_jkl(self):
+ self.testapp.get('/jkl', status=404)
+
+ def test_mno(self):
+ self.testapp.get('/mno', status=404)
+
+ def test_pqr_global2(self):
+ res = self.testapp.get('/pqr/global2', status=200)
+ self.assertEqual(res.body, 'global2')
+
+ def test_error(self):
+ res = self.testapp.get('/error', status=200)
+ self.assertEqual(res.body, 'supressed')
+
+ def test_error2(self):
+ res = self.testapp.get('/error2', status=200)
+ self.assertEqual(res.body, 'supressed2')
+
+ def test_error_sub(self):
+ res = self.testapp.get('/error_sub', status=200)
+ self.assertEqual(res.body, 'supressed2')
+
+class TestRestBugApp(IntegrationBase):
# test bug reported by delijati 2010/2/3 (http://pastebin.com/d4cc15515)
config = 'pyramid.tests.restbugapp:configure.zcml'
def test_it(self):
- import twill.commands
- browser = twill.commands.get_browser()
- browser.go('http://localhost:6543/pet')
- self.assertEqual(browser.get_code(), 200)
- self.assertEqual(browser.get_html(), 'gotten')
+ res = self.testapp.get('/pet', status=200)
+ self.assertEqual(res.body, 'gotten')
-class TestViewDecoratorApp(TwillBase):
+class TestViewDecoratorApp(IntegrationBase):
config = 'pyramid.tests.viewdecoratorapp:configure.zcml'
- def test_it(self):
- import twill.commands
- browser = twill.commands.get_browser()
- browser.go('http://localhost:6543/first')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('OK' in browser.get_html())
-
- browser.go('http://localhost:6543/second')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('OK2' in browser.get_html())
-
- browser.go('http://localhost:6543/third')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('OK3' in browser.get_html())
-
-class TestViewPermissionBug(TwillBase):
+ def _configure_mako(self):
+ tmpldir = os.path.join(os.path.dirname(__file__), 'viewdecoratorapp',
+ 'views')
+ self.config.registry.settings['mako.directories'] = tmpldir
+
+ def test_first(self):
+ # we use mako here instead of chameleon because it works on Jython
+ self._configure_mako()
+ res = self.testapp.get('/first', status=200)
+ self.failUnless('OK' in res.body)
+
+ def test_second(self):
+ # we use mako here instead of chameleon because it works on Jython
+ self._configure_mako()
+ res = self.testapp.get('/second', status=200)
+ self.failUnless('OK2' in res.body)
+
+class TestViewPermissionBug(IntegrationBase):
# view_execution_permitted bug as reported by Shane at http://lists.repoze.org/pipermail/repoze-dev/2010-October/003603.html
config = 'pyramid.tests.permbugapp:configure.zcml'
- def test_it(self):
- import twill.commands
- browser = twill.commands.get_browser()
- browser.go('http://localhost:6543/test')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('ACLDenied' in browser.get_html())
- browser.go('http://localhost:6543/x')
- self.assertEqual(browser.get_code(), 401)
-
-class TestDefaultViewPermissionBug(TwillBase):
+ def test_test(self):
+ res = self.testapp.get('/test', status=200)
+ self.failUnless('ACLDenied' in res.body)
+
+ def test_x(self):
+ self.testapp.get('/x', status=401)
+
+class TestDefaultViewPermissionBug(IntegrationBase):
# default_view_permission bug as reported by Wiggy at http://lists.repoze.org/pipermail/repoze-dev/2010-October/003602.html
config = 'pyramid.tests.defpermbugapp:configure.zcml'
- def test_it(self):
- import twill.commands
- browser = twill.commands.get_browser()
- browser.go('http://localhost:6543/x')
- self.assertEqual(browser.get_code(), 401)
- self.failUnless('failed permission check' in browser.get_html())
- browser.go('http://localhost:6543/y')
- self.assertEqual(browser.get_code(), 401)
- self.failUnless('failed permission check' in browser.get_html())
- browser.go('http://localhost:6543/z')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('public' in browser.get_html())
+ def test_x(self):
+ res = self.testapp.get('/x', status=401)
+ self.failUnless('failed permission check' in res.body)
+
+ def test_y(self):
+ res = self.testapp.get('/y', status=401)
+ self.failUnless('failed permission check' in res.body)
+
+ def test_z(self):
+ res = self.testapp.get('/z', status=200)
+ self.failUnless('public' in res.body)
from pyramid.tests.exceptionviewapp.models import AnException, NotAnException
excroot = {'anexception':AnException(),
'notanexception':NotAnException()}
-class TestExceptionViewsApp(TwillBase):
+class TestExceptionViewsApp(IntegrationBase):
config = 'pyramid.tests.exceptionviewapp:configure.zcml'
root_factory = lambda *arg: excroot
- def test_it(self):
- import twill.commands
- browser = twill.commands.get_browser()
- browser.go('http://localhost:6543/')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('maybe' in browser.get_html())
-
- browser.go('http://localhost:6543/notanexception')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('no' in browser.get_html())
-
- browser.go('http://localhost:6543/anexception')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('yes' in browser.get_html())
-
- browser.go('http://localhost:6543/route_raise_exception')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('yes' in browser.get_html())
-
- browser.go('http://localhost:6543/route_raise_exception2')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('yes' in browser.get_html())
-
- browser.go('http://localhost:6543/route_raise_exception3')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('whoa' in browser.get_html())
-
- browser.go('http://localhost:6543/route_raise_exception4')
- self.assertEqual(browser.get_code(), 200)
- self.failUnless('whoa' in browser.get_html())
+ def test_root(self):
+ res = self.testapp.get('/', status=200)
+ self.failUnless('maybe' in res.body)
+
+ def test_notanexception(self):
+ res = self.testapp.get('/notanexception', status=200)
+ self.failUnless('no' in res.body)
+
+ def test_anexception(self):
+ res = self.testapp.get('/anexception', status=200)
+ self.failUnless('yes' in res.body)
+
+ def test_route_raise_exception(self):
+ res = self.testapp.get('/route_raise_exception', status=200)
+ self.failUnless('yes' in res.body)
+
+ def test_route_raise_exception2(self):
+ res = self.testapp.get('/route_raise_exception2', status=200)
+ self.failUnless('yes' in res.body)
+
+ def test_route_raise_exception3(self):
+ res = self.testapp.get('/route_raise_exception3', status=200)
+ self.failUnless('whoa' in res.body)
+
+ def test_route_raise_exception4(self):
+ res = self.testapp.get('/route_raise_exception4', status=200)
+ self.failUnless('whoa' in res.body)
class DummyContext(object):
pass
diff --git a/pyramid/tests/test_mako_templating.py b/pyramid/tests/test_mako_templating.py
index e0c02550b..506dc18cd 100644
--- a/pyramid/tests/test_mako_templating.py
+++ b/pyramid/tests/test_mako_templating.py
@@ -47,7 +47,7 @@ class Test_renderer_factory(Base, unittest.TestCase):
def test_composite_directories_path(self):
from pyramid.mako_templating import IMakoLookup
- twice = self.templates_dir + '\n' + self.templates_dir
+ twice = '\n' + self.templates_dir + '\n' + self.templates_dir + '\n'
settings = {'mako.directories':twice}
info = DummyRendererInfo({
'name':'helloworld.mak',
@@ -59,6 +59,165 @@ class Test_renderer_factory(Base, unittest.TestCase):
lookup = self.config.registry.getUtility(IMakoLookup)
self.assertEqual(lookup.directories, [self.templates_dir]*2)
+ def test_directories_list(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':['a', 'b']}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.directories, ['a', 'b'])
+
+ def test_with_module_directory_resource_spec(self):
+ import os
+ from pyramid.mako_templating import IMakoLookup
+ module_directory = 'pyramid.tests:fixtures'
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.module_directory':module_directory}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ fixtures = os.path.join(os.path.dirname(__file__), 'fixtures')
+ self.assertEqual(lookup.module_directory, fixtures)
+
+ def test_with_module_directory_resource_abspath(self):
+ import os
+ from pyramid.mako_templating import IMakoLookup
+ fixtures = os.path.join(os.path.dirname(__file__), 'fixtures')
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.module_directory':fixtures}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.module_directory, fixtures)
+
+ def test_with_input_encoding(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.input_encoding':'utf-16'}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['input_encoding'], 'utf-16')
+
+ def test_with_error_handler(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.error_handler':'pyramid.tests'}
+ import pyramid.tests
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['error_handler'], pyramid.tests)
+
+ def test_with_default_filters(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.default_filters':'\nh\ng\n\n'}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['default_filters'], ['h', 'g'])
+
+ def test_with_default_filters_list(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.default_filters':['h', 'g']}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['default_filters'], ['h', 'g'])
+
+ def test_with_imports(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.imports':'\none\ntwo\n\n'}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['imports'], ['one', 'two'])
+
+ def test_with_imports_list(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.imports':['one', 'two']}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['imports'], ['one', 'two'])
+
+ def test_with_strict_undefined_true(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.strict_undefined':'true'}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['strict_undefined'], True)
+
+ def test_with_strict_undefined_false(self):
+ from pyramid.mako_templating import IMakoLookup
+ settings = {'mako.directories':self.templates_dir,
+ 'mako.strict_undefined':'false'}
+ info = DummyRendererInfo({
+ 'name':'helloworld.mak',
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':settings,
+ })
+ self._callFUT(info)
+ lookup = self.config.registry.getUtility(IMakoLookup)
+ self.assertEqual(lookup.template_args['strict_undefined'], False)
+
def test_with_lookup(self):
from pyramid.mako_templating import IMakoLookup
lookup = dict()
@@ -170,7 +329,8 @@ class TestIntegration(unittest.TestCase):
def test_render_to_response_pkg_spec(self):
from pyramid.renderers import render_to_response
- result = render_to_response('pyramid.tests:fixtures/helloworld.mak', {'a':1})
+ result = render_to_response('pyramid.tests:fixtures/helloworld.mak',
+ {'a':1})
self.assertEqual(result.ubody, u'\nHello föö\n')
def test_render_with_abs_path(self):
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index b4a6892db..bd6a0825d 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -23,6 +23,7 @@ class TestTemplateRendererFactory(unittest.TestCase):
'package':None,
'registry':self.config.registry,
'settings':{},
+ 'type':'type',
})
self.assertRaises(ValueError, self._callFUT, info, None)
@@ -37,6 +38,7 @@ class TestTemplateRendererFactory(unittest.TestCase):
'package':None,
'registry':self.config.registry,
'settings':{},
+ 'type':'type',
})
result = self._callFUT(info, None)
self.failUnless(result is renderer)
@@ -52,6 +54,7 @@ class TestTemplateRendererFactory(unittest.TestCase):
'package':None,
'registry':self.config.registry,
'settings':{},
+ 'type':'type',
})
result = self._callFUT(info, None)
self.failUnless(result is renderer)
@@ -66,6 +69,7 @@ class TestTemplateRendererFactory(unittest.TestCase):
'package':None,
'registry':self.config.registry,
'settings':{},
+ 'type':'type',
})
result = self._callFUT(info, None)
self.failUnless(renderer is result)
@@ -82,6 +86,7 @@ class TestTemplateRendererFactory(unittest.TestCase):
'package':pyramid.tests,
'registry':self.config.registry,
'settings':{},
+ 'type':'type',
})
result = self._callFUT(info, None)
self.failUnless(renderer is result)
@@ -93,6 +98,7 @@ class TestTemplateRendererFactory(unittest.TestCase):
'package':None,
'registry':self.config.registry,
'settings':{},
+ 'type':'type',
})
self.assertRaises(ValueError, self._callFUT, info, None)
@@ -107,6 +113,7 @@ class TestTemplateRendererFactory(unittest.TestCase):
'package':None,
'registry':self.config.registry,
'settings':{},
+ 'type':'type',
})
renderer = {}
testing.registerUtility(renderer, ITemplateRenderer, name=spec)
@@ -126,18 +133,18 @@ class TestTemplateRendererFactory(unittest.TestCase):
'package':None,
'registry':self.config.registry,
'settings':{},
+ 'type':'type',
})
result = self._callFUT(info, factory)
self.failUnless(result is renderer)
- path = os.path.abspath(__file__)
- if path.endswith('pyc'): # pragma: no cover
+ path = os.path.abspath(__file__).split('$')[0] # jython
+ if path.endswith('.pyc'): # pragma: no cover
path = path[:-1]
- self.assertEqual(factory.path, path)
+ self.failUnless(factory.path.startswith(path))
self.assertEqual(factory.kw, {})
def test_reload_resources_true(self):
import pyramid.tests
- from pyramid.threadlocal import get_current_registry
from pyramid.interfaces import ISettings
from pyramid.interfaces import ITemplateRenderer
settings = {'reload_resources':True}
@@ -145,43 +152,44 @@ class TestTemplateRendererFactory(unittest.TestCase):
renderer = {}
factory = DummyFactory(renderer)
spec = 'test_renderers.py'
+ reg = self.config.registry
info = DummyRendererInfo({
'name':spec,
'package':pyramid.tests,
- 'registry':self.config.registry,
- 'settings':{},
+ 'registry':reg,
+ 'settings':settings,
+ 'type':'type',
})
result = self._callFUT(info, factory)
self.failUnless(result is renderer)
spec = '%s:%s' % ('pyramid.tests', 'test_renderers.py')
- reg = get_current_registry()
self.assertEqual(reg.queryUtility(ITemplateRenderer, name=spec),
None)
def test_reload_resources_false(self):
import pyramid.tests
- from pyramid.threadlocal import get_current_registry
from pyramid.interfaces import ITemplateRenderer
settings = {'reload_resources':False}
renderer = {}
factory = DummyFactory(renderer)
spec = 'test_renderers.py'
+ reg = self.config.registry
info = DummyRendererInfo({
'name':spec,
'package':pyramid.tests,
- 'registry':self.config.registry,
+ 'registry':reg,
'settings':settings,
+ 'type':'type',
})
result = self._callFUT(info, factory)
self.failUnless(result is renderer)
spec = '%s:%s' % ('pyramid.tests', 'test_renderers.py')
- reg = get_current_registry()
self.assertNotEqual(reg.queryUtility(ITemplateRenderer, name=spec),
None)
class TestRendererFromName(unittest.TestCase):
def setUp(self):
- cleanUp()
+ self.config = cleanUp()
def tearDown(self):
cleanUp()
@@ -191,8 +199,7 @@ class TestRendererFromName(unittest.TestCase):
return renderer_from_name(path, package)
def test_it(self):
- from pyramid.threadlocal import get_current_registry
- registry = get_current_registry()
+ registry = self.config.registry
settings = {}
registry.settings = settings
from pyramid.interfaces import IRendererFactory
@@ -211,8 +218,7 @@ class TestRendererFromName(unittest.TestCase):
def test_it_with_package(self):
import pyramid
- from pyramid.threadlocal import get_current_registry
- registry = get_current_registry()
+ registry = self.config.registry
settings = {}
registry.settings = settings
from pyramid.interfaces import IRendererFactory
@@ -534,7 +540,7 @@ class DummyFactory:
def __init__(self, renderer):
self.renderer = renderer
- def __call__(self, path, **kw):
+ def __call__(self, path, lookup, **kw):
self.path = path
self.kw = kw
return self.renderer
diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py
index d12b47642..ab985694b 100644
--- a/pyramid/tests/test_request.py
+++ b/pyramid/tests/test_request.py
@@ -266,6 +266,41 @@ class TestRequest(unittest.TestCase):
self.assertEqual(result,
'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo')
+ def test_route_path(self):
+ environ = {
+ 'PATH_INFO':'/',
+ 'SERVER_NAME':'example.com',
+ 'SERVER_PORT':'5432',
+ 'QUERY_STRING':'la=La%20Pe%C3%B1a',
+ 'wsgi.url_scheme':'http',
+ }
+ from pyramid.interfaces import IRoutesMapper
+ inst = self._makeOne(environ)
+ mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3'))
+ self.config.registry.registerUtility(mapper, IRoutesMapper)
+ result = inst.route_path('flub', 'extra1', 'extra2',
+ a=1, b=2, c=3, _query={'a':1},
+ _anchor=u"foo")
+ self.assertEqual(result, '/1/2/3/extra1/extra2?a=1#foo')
+
+ def test_static_url(self):
+ from pyramid.interfaces import IStaticURLInfo
+ environ = {
+ 'PATH_INFO':'/',
+ 'SERVER_NAME':'example.com',
+ 'SERVER_PORT':'5432',
+ 'QUERY_STRING':'',
+ 'wsgi.url_scheme':'http',
+ }
+ request = self._makeOne(environ)
+ info = DummyStaticURLInfo('abc')
+ self.config.registry.registerUtility(info, IStaticURLInfo)
+ result = request.static_url('pyramid.tests:static/foo.css')
+ self.assertEqual(result, 'abc')
+ self.assertEqual(info.args,
+ ('pyramid.tests:static/foo.css', request, {}) )
+
+
class Test_route_request_iface(unittest.TestCase):
def _callFUT(self, name):
from pyramid.request import route_request_iface
@@ -323,3 +358,11 @@ class DummyRoute:
def generate(self, kw):
self.kw = kw
return self.result
+
+class DummyStaticURLInfo:
+ def __init__(self, result):
+ self.result = result
+
+ def generate(self, path, request, **kw):
+ self.args = path, request, kw
+ return self.result
diff --git a/pyramid/tests/test_settings.py b/pyramid/tests/test_settings.py
index 12b1174de..49c1e928f 100644
--- a/pyramid/tests/test_settings.py
+++ b/pyramid/tests/test_settings.py
@@ -184,9 +184,13 @@ class TestGetSettings(unittest.TestCase):
registry = Registry('testing')
self.config = Configurator(registry=registry)
self.config.begin()
+ from zope.deprecation import __show__
+ __show__.off()
def tearDown(self):
self.config.end()
+ from zope.deprecation import __show__
+ __show__.on()
def _callFUT(self):
from pyramid.settings import get_settings
diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py
index 8336bcec5..f05e7fc01 100644
--- a/pyramid/tests/test_testing.py
+++ b/pyramid/tests/test_testing.py
@@ -693,6 +693,30 @@ class TestMockTemplate(unittest.TestCase):
template = self._makeOne('123')
self.assertEqual(template(), '123')
+class Test_skip_on(unittest.TestCase):
+ def setUp(self):
+ from pyramid.testing import skip_on
+ self.os_name = skip_on.os_name
+ skip_on.os_name = 'wrong'
+
+ def tearDown(self):
+ from pyramid.testing import skip_on
+ skip_on.os_name = self.os_name
+
+ def _callFUT(self, *platforms):
+ from pyramid.testing import skip_on
+ return skip_on(*platforms)
+
+ def test_wrong_platform(self):
+ def foo(): return True
+ decorated = self._callFUT('wrong')(foo)
+ self.assertEqual(decorated(), None)
+
+ def test_ok_platform(self):
+ def foo(): return True
+ decorated = self._callFUT('ok')(foo)
+ self.assertEqual(decorated(), True)
+
from zope.interface import Interface
from zope.interface import implements
diff --git a/pyramid/tests/test_traversal.py b/pyramid/tests/test_traversal.py
index 2deb5982c..3245d6302 100644
--- a/pyramid/tests/test_traversal.py
+++ b/pyramid/tests/test_traversal.py
@@ -522,6 +522,32 @@ class FindModelTests(unittest.TestCase):
self.assertEqual(root.wascontext, True)
self.assertEqual(root.request.environ['PATH_INFO'], '/')
+ def test_absolute_unicode_found(self):
+ # test for bug wiggy found in wild, traceback stack:
+ # root = u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF'
+ # wiggy's code: section=find_model(page, root)
+ # find_model L76: D = traverse(model, path)
+ # traverse L291: return traverser(request)
+ # __call__ line 568: vpath_tuple = traversal_path(vpath)
+ # lru_cached line 91: f(*arg)
+ # traversal_path line 443: path.encode('ascii')
+ # UnicodeEncodeError: 'ascii' codec can't encode characters in
+ # position 1-12: ordinal not in range(128)
+ #
+ # solution: encode string to ascii in pyramid.traversal.traverse
+ # before passing it along to webob as path_info
+ from pyramid.traversal import ModelGraphTraverser
+ unprintable = DummyContext()
+ root = DummyContext(unprintable)
+ unprintable.__parent__ = root
+ unprintable.__name__ = unicode(
+ '/\xe6\xb5\x81\xe8\xa1\x8c\xe8\xb6\x8b\xe5\x8a\xbf', 'utf-8')
+ root.__parent__ = None
+ root.__name__ = None
+ traverser = ModelGraphTraverser
+ self._registerTraverser(traverser)
+ result = self._callFUT(root, u'/%E6%B5%81%E8%A1%8C%E8%B6%8B%E5%8A%BF')
+ self.assertEqual(result, unprintable)
class ModelPathTests(unittest.TestCase):
def _callFUT(self, model, *elements):
diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py
index 332ff3f11..aad969ca7 100644
--- a/pyramid/tests/test_url.py
+++ b/pyramid/tests/test_url.py
@@ -209,6 +209,27 @@ class TestRouteUrl(unittest.TestCase):
self.assertEqual(result, 'http://example2.com/1/2/3/a')
self.assertEqual(route.kw, {}) # shouldnt have anchor/query
+class TestRoutePath(unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
+ def _callFUT(self, *arg, **kw):
+ from pyramid.url import route_path
+ return route_path(*arg, **kw)
+
+ def test_with_elements(self):
+ from pyramid.interfaces import IRoutesMapper
+ request = _makeRequest()
+ mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3'))
+ request.registry.registerUtility(mapper, IRoutesMapper)
+ result = self._callFUT('flub', request, 'extra1', 'extra2',
+ a=1, b=2, c=3, _query={'a':1},
+ _anchor=u"foo")
+ self.assertEqual(result, '/1/2/3/extra1/extra2?a=1#foo')
+
class TestStaticUrl(unittest.TestCase):
def setUp(self):
cleanUp()
diff --git a/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py
index 799f4986f..12c5cf220 100644
--- a/pyramid/tests/test_urldispatch.py
+++ b/pyramid/tests/test_urldispatch.py
@@ -251,6 +251,13 @@ class TestCompileRoute(unittest.TestCase):
from pyramid.exceptions import URLDecodeError
matcher, generator = self._callFUT('/:foo')
self.assertRaises(URLDecodeError, matcher, '/%FF%FE%8B%00')
+
+ def test_custom_regex(self):
+ matcher, generator = self._callFUT('foo/{baz}/biz/{buz:[^/\.]+}.{bar}')
+ self.assertEqual(matcher('/foo/baz/biz/buz.bar'),
+ {'baz':'baz', 'buz':'buz', 'bar':'bar'})
+ self.assertEqual(matcher('foo/baz/biz/buz/bar'), None)
+ self.assertEqual(generator({'baz':1, 'buz':2, 'bar': 'html'}), '/foo/1/biz/2.html')
class TestCompileRouteMatchFunctional(unittest.TestCase):
def matches(self, pattern, path, expected):
@@ -271,7 +278,6 @@ class TestCompileRouteMatchFunctional(unittest.TestCase):
self.matches('/:x', '', None)
self.matches('/:x', '/', None)
self.matches('/abc/:def', '/abc/', None)
- self.matches('/abc/:def:baz', '/abc/bleep', None) # bad pattern
self.matches('', '/', {})
self.matches('/', '/', {})
self.matches('/:x', '/a', {'x':'a'})
diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py
new file mode 100644
index 000000000..2929f888f
--- /dev/null
+++ b/pyramid/tests/test_util.py
@@ -0,0 +1,177 @@
+import unittest
+
+class TestDottedNameResolver(unittest.TestCase):
+ def _makeOne(self, package=None):
+ from pyramid.util import DottedNameResolver
+ return DottedNameResolver(package)
+
+ def config_exc(self, func, *arg, **kw):
+ from pyramid.exceptions import ConfigurationError
+ try:
+ func(*arg, **kw)
+ except ConfigurationError, e:
+ return e
+ else:
+ raise AssertionError('Invalid not raised') # pragma: no cover
+
+ def test_zope_dottedname_style_resolve_absolute(self):
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style(
+ 'pyramid.tests.test_util.TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test_zope_dottedname_style_irrresolveable_absolute(self):
+ typ = self._makeOne()
+ self.assertRaises(ImportError, typ._zope_dottedname_style,
+ 'pyramid.test_util.nonexisting_name')
+
+ def test__zope_dottedname_style_resolve_relative(self):
+ import pyramid.tests
+ typ = self._makeOne(package=pyramid.tests)
+ result = typ._zope_dottedname_style(
+ '.test_util.TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test__zope_dottedname_style_resolve_relative_leading_dots(self):
+ import pyramid.tests.test_configuration
+ typ = self._makeOne(package=pyramid.tests)
+ result = typ._zope_dottedname_style(
+ '..tests.test_util.TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test__zope_dottedname_style_resolve_relative_is_dot(self):
+ import pyramid.tests
+ typ = self._makeOne(package=pyramid.tests)
+ result = typ._zope_dottedname_style('.')
+ self.assertEqual(result, pyramid.tests)
+
+ def test__zope_dottedname_style_irresolveable_relative_is_dot(self):
+ typ = self._makeOne()
+ e = self.config_exc(typ._zope_dottedname_style, '.')
+ self.assertEqual(
+ e.args[0],
+ "relative name '.' irresolveable without package")
+
+ def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self):
+ typ = self._makeOne()
+ e = self.config_exc(typ._zope_dottedname_style, '.whatever')
+ self.assertEqual(
+ e.args[0],
+ "relative name '.whatever' irresolveable without package")
+
+ def test_zope_dottedname_style_irrresolveable_relative(self):
+ import pyramid.tests
+ typ = self._makeOne(package=pyramid.tests)
+ self.assertRaises(ImportError, typ._zope_dottedname_style,
+ '.notexisting')
+
+ def test__zope_dottedname_style_resolveable_relative(self):
+ import pyramid
+ typ = self._makeOne(package=pyramid)
+ result = typ._zope_dottedname_style('.tests')
+ from pyramid import tests
+ self.assertEqual(result, tests)
+
+ def test__zope_dottedname_style_irresolveable_absolute(self):
+ typ = self._makeOne()
+ self.assertRaises(
+ ImportError,
+ typ._zope_dottedname_style, 'pyramid.fudge.bar')
+
+ def test__zope_dottedname_style_resolveable_absolute(self):
+ typ = self._makeOne()
+ result = typ._zope_dottedname_style(
+ 'pyramid.tests.test_util.TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test__pkg_resources_style_resolve_absolute(self):
+ typ = self._makeOne()
+ result = typ._pkg_resources_style(
+ 'pyramid.tests.test_util:TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test__pkg_resources_style_irrresolveable_absolute(self):
+ typ = self._makeOne()
+ self.assertRaises(ImportError, typ._pkg_resources_style,
+ 'pyramid.tests:nonexisting')
+
+ def test__pkg_resources_style_resolve_relative(self):
+ import pyramid.tests
+ typ = self._makeOne(package=pyramid.tests)
+ result = typ._pkg_resources_style(
+ '.test_util:TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test__pkg_resources_style_resolve_relative_is_dot(self):
+ import pyramid.tests
+ typ = self._makeOne(package=pyramid.tests)
+ result = typ._pkg_resources_style('.')
+ self.assertEqual(result, pyramid.tests)
+
+ def test__pkg_resources_style_resolve_relative_nocurrentpackage(self):
+ typ = self._makeOne()
+ from pyramid.exceptions import ConfigurationError
+ self.assertRaises(ConfigurationError, typ._pkg_resources_style,
+ '.whatever')
+
+ def test__pkg_resources_style_irrresolveable_relative(self):
+ import pyramid
+ typ = self._makeOne(package=pyramid)
+ self.assertRaises(ImportError, typ._pkg_resources_style,
+ ':notexisting')
+
+ def test_resolve_not_a_string(self):
+ typ = self._makeOne()
+ e = self.config_exc(typ.resolve, None)
+ self.assertEqual(e.args[0], 'None is not a string')
+
+ def test_resolve_using_pkgresources_style(self):
+ typ = self._makeOne()
+ result = typ.resolve(
+ 'pyramid.tests.test_util:TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test_resolve_using_zope_dottedname_style(self):
+ typ = self._makeOne()
+ result = typ.resolve(
+ 'pyramid.tests.test_util:TestDottedNameResolver')
+ self.assertEqual(result, self.__class__)
+
+ def test_resolve_missing_raises(self):
+ typ = self._makeOne()
+ self.assertRaises(ImportError, typ.resolve, 'cant.be.found')
+
+ def test_ctor_string_module_resolveable(self):
+ import pyramid.tests
+ typ = self._makeOne('pyramid.tests.test_util')
+ self.assertEqual(typ.package, pyramid.tests)
+ self.assertEqual(typ.package_name, 'pyramid.tests')
+
+ def test_ctor_string_package_resolveable(self):
+ import pyramid.tests
+ typ = self._makeOne('pyramid.tests')
+ self.assertEqual(typ.package, pyramid.tests)
+ self.assertEqual(typ.package_name, 'pyramid.tests')
+
+ def test_ctor_string_irresolveable(self):
+ from pyramid.configuration import ConfigurationError
+ self.assertRaises(ConfigurationError, self._makeOne, 'cant.be.found')
+
+ def test_ctor_module(self):
+ import pyramid.tests
+ import pyramid.tests.test_util
+ typ = self._makeOne(pyramid.tests.test_util)
+ self.assertEqual(typ.package, pyramid.tests)
+ self.assertEqual(typ.package_name, 'pyramid.tests')
+
+ def test_ctor_package(self):
+ import pyramid.tests
+ typ = self._makeOne(pyramid.tests)
+ self.assertEqual(typ.package, pyramid.tests)
+ self.assertEqual(typ.package_name, 'pyramid.tests')
+
+ def test_ctor_None(self):
+ typ = self._makeOne(None)
+ self.assertEqual(typ.package, None)
+ self.assertEqual(typ.package_name, None)
+
diff --git a/pyramid/tests/test_zcml.py b/pyramid/tests/test_zcml.py
index f4a6e81b6..905a53287 100644
--- a/pyramid/tests/test_zcml.py
+++ b/pyramid/tests/test_zcml.py
@@ -803,6 +803,8 @@ class TestZCMLConfigure(unittest.TestCase):
return zcml_configure(path, package)
def setUp(self):
+ from zope.deprecation import __show__
+ __show__.off()
testing.setUp()
self.tempdir = None
import sys
@@ -822,6 +824,8 @@ class TestZCMLConfigure(unittest.TestCase):
self.tempdir = tempdir
def tearDown(self):
+ from zope.deprecation import __show__
+ __show__.on()
testing.tearDown()
import sys
import shutil
diff --git a/pyramid/tests/viewdecoratorapp/views/templates/foo.pt b/pyramid/tests/viewdecoratorapp/views/templates/foo.mak
index 6a2f701b6..6a2f701b6 100644
--- a/pyramid/tests/viewdecoratorapp/views/templates/foo.pt
+++ b/pyramid/tests/viewdecoratorapp/views/templates/foo.mak
diff --git a/pyramid/tests/viewdecoratorapp/views/views.py b/pyramid/tests/viewdecoratorapp/views/views.py
index c59bc87ed..0b3147c86 100644
--- a/pyramid/tests/viewdecoratorapp/views/views.py
+++ b/pyramid/tests/viewdecoratorapp/views/views.py
@@ -1,17 +1,11 @@
-import os
from pyramid.view import view_config
-@view_config(renderer='templates/foo.pt', name='first')
+@view_config(renderer='templates/foo.mak', name='first')
def first(request):
return {'result':'OK1'}
-@view_config(renderer='pyramid.tests.viewdecoratorapp.views:templates/foo.pt',
+@view_config(renderer='pyramid.tests.viewdecoratorapp.views:templates/foo.mak',
name='second')
def second(request):
return {'result':'OK2'}
-here = os.path.normpath(os.path.dirname(os.path.abspath(__file__)))
-foo = os.path.join(here, 'templates', 'foo.pt')
-@view_config(renderer=foo, name='third')
-def third(request):
- return {'result':'OK3'}
diff --git a/pyramid/traversal.py b/pyramid/traversal.py
index e928c33f7..fb73ad906 100644
--- a/pyramid/traversal.py
+++ b/pyramid/traversal.py
@@ -228,7 +228,7 @@ def traverse(model, path):
object supplied to the function as the ``model`` argument. If an
empty string is passed as ``path``, the ``model`` passed in will
be returned. Model path strings must be escaped in the following
- manner: each Unicode path segment must be encoded as UTF-8 and as
+ manner: each Unicode path segment must be encoded as UTF-8 and
each path segment must escaped via Python's :mod:`urllib.quote`.
For example, ``/path/to%20the/La%20Pe%C3%B1a`` (absolute) or
``to%20the/La%20Pe%C3%B1a`` (relative). The
@@ -272,6 +272,17 @@ def traverse(model, path):
else:
path = ''
+ # The user is supposed to pass us a string object, never Unicode. In
+ # practice, however, users indeed pass Unicode to this API. If they do
+ # pass a Unicode object, its data *must* be entirely encodeable to ASCII,
+ # so we encode it here as a convenience to the user and to prevent
+ # second-order failures from cropping up (all failures will occur at this
+ # step rather than later down the line as the result of calling
+ # ``traversal_path``).
+
+ if isinstance(path, unicode):
+ path = path.encode('ascii')
+
if path and path[0] == '/':
model = find_root(model)
diff --git a/pyramid/url.py b/pyramid/url.py
index b740aaca7..2e73e9cf5 100644
--- a/pyramid/url.py
+++ b/pyramid/url.py
@@ -18,6 +18,9 @@ def route_url(route_name, request, *elements, **kw):
"""Generates a fully qualified URL for a named :app:`Pyramid`
:term:`route configuration`.
+ .. note:: Calling :meth:`pyramid.Request.route_url` can be used to
+ achieve the same result as :func:`pyramid.url.route_url`.
+
Use the route's ``name`` as the first positional argument. Use a
request object as the second positional argument. Additional
positional arguments are appended to the URL as path segments
@@ -29,7 +32,7 @@ def route_url(route_name, request, *elements, **kw):
enough arguments, for example).
For example, if you've defined a route named "foobar" with the path
- ``:foo/:bar/*traverse``::
+ ``:foo/{bar}/*traverse``::
route_url('foobar', request, foo='1') => <KeyError exception>
route_url('foobar', request, foo='1', bar='2') => <KeyError exception>
@@ -102,6 +105,7 @@ def route_url(route_name, request, *elements, **kw):
If the route object which matches the ``route_name`` argument has
a :term:`pregenerator`, the ``*elements`` and ``**kw`` arguments
arguments passed to this function might be augmented or changed.
+
"""
try:
reg = request.registry
@@ -149,6 +153,34 @@ def route_url(route_name, request, *elements, **kw):
return app_url + path + suffix + qs + anchor
+def route_path(route_name, request, *elements, **kw):
+ """Generates a path (aka a 'relative URL', a URL minus the host, scheme,
+ and port) for a named :app:`Pyramid` :term:`route configuration`.
+
+ .. note:: Calling :meth:`pyramid.Request.route_path` can be used to
+ achieve the same result as :func:`pyramid.url.route_path`.
+
+ This function accepts the same argument as :func:`pyramid.url.route_url`
+ and performs the same duty. It just omits the host, port, and scheme
+ information in the return value; only the path, query parameters,
+ and anchor data are present in the returned string.
+
+ For example, if you've defined a route named 'foobar' with the path
+ ``/{foo}/{bar}``, this call to ``route_path``::
+
+ route_path('foobar', request, foo='1', bar='2')
+
+ Will return the string ``/1/2``.
+
+ .. note:: Calling ``route_path('route', request)`` is the same as calling
+ ``route_url('route', request, _app_url='')``. ``route_path`` is, in
+ fact, implemented in terms of ``route_url`` in just this way. As a
+ result, any ``_app_url`` pass within the ``**kw`` values to
+ ``route_path`` will be ignored.
+ """
+ kw['_app_url'] = ''
+ return route_url(route_name, request, *elements, **kw)
+
def model_url(model, request, *elements, **kw):
"""
Generate a string representing the absolute URL of the ``model``
@@ -157,6 +189,9 @@ def model_url(model, request, *elements, **kw):
overall result of this function is always a UTF-8 encoded string
(never Unicode).
+ .. note:: Calling :meth:`pyramid.Request.model_url` can be used to
+ achieve the same result as :func:`pyramid.url.model_url`.
+
Examples::
model_url(context, request) =>
@@ -270,6 +305,9 @@ def static_url(path, request, **kw):
:term:`configuration declaration` or the ``<static>`` ZCML
directive (see :ref:`static_resources_section`).
+ .. note:: Calling :meth:`pyramid.Request.static_url` can be used to
+ achieve the same result as :func:`pyramid.url.static_url`.
+
Example::
static_url('mypackage:static/foo.css', request) =>
diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py
index aa0bddfe9..0f8691a07 100644
--- a/pyramid/urldispatch.py
+++ b/pyramid/urldispatch.py
@@ -74,8 +74,16 @@ class RoutesMapper(object):
return {'route':None, 'match':None}
# stolen from bobo and modified
-route_re = re.compile(r'(/:[a-zA-Z]\w*)')
+old_route_re = re.compile(r'(\:[a-zA-Z]\w*)')
+route_re = re.compile(r'(\{[a-zA-Z][^\}]*\})')
+def update_pattern(matchobj):
+ name = matchobj.group(0)
+ return '{%s}' % name[1:]
+
def _compile_route(route):
+ if old_route_re.search(route) and not route_re.search(route):
+ route = old_route_re.sub(update_pattern, route)
+
if not route.startswith('/'):
route = '/' + route
star = None
@@ -91,9 +99,13 @@ def _compile_route(route):
gen.append(prefix)
while pat:
name = pat.pop()
- name = name[2:]
- gen.append('/%%(%s)s' % name)
- name = '/(?P<%s>[^/]+)' % name
+ name = name[1:-1]
+ if ':' in name:
+ name, reg = name.split(':')
+ else:
+ reg = '[^/]+'
+ gen.append('%%(%s)s' % name)
+ name = '(?P<%s>%s)' % (name, reg)
rpat.append(name)
s = pat.pop()
if s:
diff --git a/pyramid/util.py b/pyramid/util.py
new file mode 100644
index 000000000..84a23bb23
--- /dev/null
+++ b/pyramid/util.py
@@ -0,0 +1,144 @@
+import pkg_resources
+import sys
+
+from pyramid.exceptions import ConfigurationError
+from pyramid.path import package_of
+
+class DottedNameResolver(object):
+ """ This class resolves dotted name references to 'global' Python
+ objects (objects which can be imported) to those objects.
+
+ Two dotted name styles are supported during deserialization:
+
+ - ``pkg_resources``-style dotted names where non-module attributes
+ of a package are separated from the rest of the path using a ':'
+ e.g. ``package.module:attr``.
+
+ - ``zope.dottedname``-style dotted names where non-module
+ attributes of a package are separated from the rest of the path
+ using a '.' e.g. ``package.module.attr``.
+
+ These styles can be used interchangeably. If the serialization
+ contains a ``:`` (colon), the ``pkg_resources`` resolution
+ mechanism will be chosen, otherwise the ``zope.dottedname``
+ resolution mechanism will be chosen.
+
+ The constructor accepts a single argument named ``package`` which
+ should be a one of:
+
+ - a Python module or package object
+
+ - A fully qualified (not relative) dotted name to a module or package
+
+ - The value ``None``
+
+ The ``package`` is used when relative dotted names are supplied to
+ the resolver's ``resolve`` and ``maybe_resolve`` methods. A
+ dotted name which has a ``.`` (dot) or ``:`` (colon) as its first
+ character is treated as relative.
+
+ If the value ``None`` is supplied as the package name, the
+ resolver will only be able to resolve fully qualified (not
+ relative) names. Any attempt to resolve a relative name when the
+ ``package`` is ``None`` will result in an
+ :exc:`pyramid.configuration.ConfigurationError` exception.
+
+ If a *module* or *module name* (as opposed to a package or package
+ name) is supplied as ``package``, its containing package is
+ computed and this package used to derive the package name (all
+ names are resolved relative to packages, never to modules). For
+ example, if the ``package`` argument to this type was passed the
+ string ``xml.dom.expatbuilder``, and ``.mindom`` is supplied to
+ the ``resolve`` method, the resulting import would be for
+ ``xml.minidom``, because ``xml.dom.expatbuilder`` is a module
+ object, not a package object.
+
+ If a *package* or *package name* (as opposed to a module or module
+ name) is supplied as ``package``, this package will be used to
+ relative compute dotted names. For example, if the ``package``
+ argument to this type was passed the string ``xml.dom``, and
+ ``.minidom`` is supplied to the ``resolve`` method, the resulting
+ import would be for ``xml.minidom``.
+
+ When a dotted name cannot be resolved, a
+ :class:`pyramid.exceptions.ConfigurationError` error is raised.
+ """
+ def __init__(self, package):
+ if package is None:
+ self.package_name = None
+ self.package = None
+ else:
+ if isinstance(package, basestring):
+ try:
+ __import__(package)
+ except ImportError:
+ raise ConfigurationError(
+ 'The dotted name %r cannot be imported' % (package,))
+ package = sys.modules[package]
+ self.package = package_of(package)
+ self.package_name = self.package.__name__
+
+ def _pkg_resources_style(self, value):
+ """ package.module:attr style """
+ if value.startswith('.') or value.startswith(':'):
+ if not self.package_name:
+ raise ConfigurationError(
+ 'relative name %r irresolveable without '
+ 'package_name' % (value,))
+ if value in ['.', ':']:
+ value = self.package_name
+ else:
+ value = self.package_name + value
+ return pkg_resources.EntryPoint.parse(
+ 'x=%s' % value).load(False)
+
+ def _zope_dottedname_style(self, value):
+ """ package.module.attr style """
+ module = self.package_name and self.package_name or None
+ if value == '.':
+ if self.package_name is None:
+ raise ConfigurationError(
+ 'relative name %r irresolveable without package' % (value,)
+ )
+ name = module.split('.')
+ else:
+ name = value.split('.')
+ if not name[0]:
+ if module is None:
+ raise ConfigurationError(
+ 'relative name %r irresolveable without '
+ 'package' % (value,)
+ )
+ module = module.split('.')
+ name.pop(0)
+ while not name[0]:
+ module.pop()
+ name.pop(0)
+ name = module + name
+
+ used = name.pop(0)
+ found = __import__(used)
+ for n in name:
+ used += '.' + n
+ try:
+ found = getattr(found, n)
+ except AttributeError:
+ __import__(used)
+ found = getattr(found, n) # pragma: no cover
+
+ return found
+
+ def resolve(self, dotted):
+ if not isinstance(dotted, basestring):
+ raise ConfigurationError('%r is not a string' % (dotted,))
+ return self.maybe_resolve(dotted)
+
+ def maybe_resolve(self, dotted):
+ if isinstance(dotted, basestring):
+ if ':' in dotted:
+ return self._pkg_resources_style(dotted)
+ else:
+ return self._zope_dottedname_style(dotted)
+ return dotted
+
+
diff --git a/pyramid/view.py b/pyramid/view.py
index ee2774aca..2ac51406e 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -177,7 +177,7 @@ class view_config(object):
:class:`pyramid.view.bfg_view`.
The following arguments are supported as arguments to
- :class:`pyramid.view.view_config``: ``context``, ``permission``,
+ :class:`pyramid.view.view_config`: ``context``, ``permission``,
``name``, ``request_type``, ``route_name``, ``request_method``,
``request_param``, ``containment``, ``xhr``, ``accept``,
``header`` and ``path_info``.
diff --git a/pyramid/zcml.py b/pyramid/zcml.py
index a2fdec314..3104ebac0 100644
--- a/pyramid/zcml.py
+++ b/pyramid/zcml.py
@@ -6,6 +6,8 @@ from zope.configuration.fields import GlobalInterface
from zope.configuration.fields import GlobalObject
from zope.configuration.fields import Tokens
+from zope.deprecation import deprecated
+
from zope.interface import Interface
from zope.interface import implementedBy
from zope.interface import providedBy
@@ -937,6 +939,16 @@ def zcml_configure(name, package):
file_configure = zcml_configure # backwards compat (>0.8.1)
+deprecated(
+ 'zcml_configure',
+ '(pyramid.zcml.zcml_configure is deprecated as of Pyramid 1.0. Use'
+ '``pyramid.configuration.Configurator.load_zcml`` instead.) ')
+
+deprecated(
+ 'file_configure',
+ '(pyramid.zcml.file_configure is deprecated as of Pyramid 1.0. Use'
+ '``pyramid.configuration.Configurator.load_zcml`` instead.) ')
+
def _rolledUpFactory(factories):
def factory(ob):
for f in factories:
diff --git a/setup.py b/setup.py
index b220d2914..75f5b13dd 100644
--- a/setup.py
+++ b/setup.py
@@ -29,7 +29,7 @@ except IOError:
install_requires=[
'Chameleon >= 1.2.3',
- 'Mako',
+ 'Mako >= 0.3.6', # strict_undefined
'Paste > 1.7', # temp version pin to prevent PyPi install failure :-(
'PasteDeploy',
'PasteScript',
@@ -45,10 +45,10 @@ install_requires=[
]
if platform.system() == 'Java':
- tests_require = install_requires + ['twill']
+ tests_require = install_requires + ['WebTest']
else:
- tests_require= install_requires + ['Sphinx', 'docutils', 'coverage',
- 'twill', 'repoze.sphinx.autointerface']
+ tests_require= install_requires + ['Sphinx', 'docutils',
+ 'WebTest', 'repoze.sphinx.autointerface']
if sys.version_info[:2] < (2, 6):
install_requires.append('simplejson')