summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CHANGES.txt94
-rw-r--r--CONTRIBUTORS.txt5
-rw-r--r--TODO.txt22
-rw-r--r--docs/api/authentication.rst10
-rw-r--r--docs/api/interfaces.rst4
-rw-r--r--docs/api/security.rst2
-rw-r--r--docs/designdefense.rst16
-rw-r--r--docs/glossary.rst7
-rw-r--r--docs/narr/project.rst8
-rw-r--r--docs/narr/templates.rst2
-rw-r--r--pyramid/authentication.py27
-rw-r--r--pyramid/chameleon_text.py33
-rw-r--r--pyramid/config.py764
-rw-r--r--pyramid/httpexceptions.py1
-rw-r--r--pyramid/interfaces.py36
-rw-r--r--pyramid/renderers.py12
-rw-r--r--pyramid/request.py2
-rw-r--r--pyramid/security.py18
-rw-r--r--pyramid/testing.py8
-rw-r--r--pyramid/tests/test_authentication.py33
-rw-r--r--pyramid/tests/test_chameleon_text.py25
-rw-r--r--pyramid/tests/test_chameleon_zpt.py17
-rw-r--r--pyramid/tests/test_config.py1502
-rw-r--r--pyramid/tests/test_security.py33
-rw-r--r--pyramid/tests/test_testing.py6
-rw-r--r--pyramid/tests/test_view.py16
-rw-r--r--pyramid/url.py6
-rw-r--r--pyramid/view.py16
-rw-r--r--pyramid/zcml.py5
30 files changed, 1681 insertions, 1050 deletions
diff --git a/.gitignore b/.gitignore
index ae0b17b8e..562abec68 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
.coverage
tutorial.db
env26/
+env26-debug/
env24/
env27/
jyenv/
diff --git a/CHANGES.txt b/CHANGES.txt
index 71c55fd1f..6471dd7a8 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -9,8 +9,69 @@ Bug Fixes
Instead of trying to resolve the view, if it cannot, it will now just print
``<unknown>``.
-- The `self` argument was included in new methods of the ISession interface
- signature.
+- The `self` argument was included in new methods of the ``ISession`` interface
+ signature, causing ``pyramid_beaker`` tests to fail (jkrebs).
+
+Features
+--------
+
+- ``config.add_view`` now accepts a ``decorator`` keyword argument, a callable
+ which will decorate the view callable before it is added to the registry.
+
+- If a handler class provides an ``__action_decorator__`` attribute (usually
+ a classmethod or staticmethod), use that as the decorator for each view
+ registration for that handler.
+
+- The ``pyramid.interfaces.IAuthenticationPolicy`` interface now specifies an
+ ``unauthenticated_userid`` method. This method supports an important
+ optimization required by people who are using persistent storages which do
+ not support object caching and whom want to create a "user object" as a
+ request attribute.
+
+- A new API has been added to the ``pyramid.security`` module named
+ ``unauthenticated_userid``. This API function calls the
+ ``unauthenticated_userid`` method of the effective security policy.
+
+- An ``unauthenticated_userid`` method has been added to the dummy
+ authentication policy returned by
+ ``pyramid.config.Configurator.testing_securitypolicy``. It returns the
+ same thing as that the dummy authentication policy's
+ ``authenticated_userid`` method.
+
+- The class ``pyramid.authentication.AuthTktCookieHelper`` is now an API.
+ This class can be used by third-party authentication policy developers to
+ help in the mechanics of authentication cookie-setting.
+
+- New constructor argument to Configurator: ``default_view_mapper``. Useful
+ to create systems that have alternate view calling conventions. A view
+ mapper allows objects that are meant to be used as view callables to have
+ an arbitrary argument list and an arbitrary result. The object passed as
+ ``default_view_mapper`` should implement the
+ ``pyramid.interfaces.IViewMapperFactory`` interface.
+
+- add a ``set_view_mapper`` API to Configurator. Has
+ the same result as passing ``default_view_mapper`` to the Configurator
+ constructor.
+
+- ``config.add_view`` now accepts a ``view_mapper`` keyword argument, which
+ should either be ``None``, a string representing a Python dotted name, or
+ an object which is an ``IViewMapperFactory``. This feature is not useful
+ for "civilians", only for extension writers.
+
+- Allow static renderer provided during view registration to be overridden at
+ request time via a request attribute named ``override_renderer``, which
+ should be the name of a previously registered renderer. Useful to provide
+ "omnipresent" RPC using existing rendered views.
+
+Backwards Incompatibilities
+---------------------------
+
+- Since the ``pyramid.interfaces.IAuthenticationPolicy`` interface now
+ specifies that a policy implementation must implement an
+ ``unauthenticated_userid`` method, all third-party custom authentication
+ policies now must implement this method. It, however, will only be called
+ when the global function named ``pyramid.security.unauthenticated_userid``
+ is invoked, so if you're not invoking that, you will not notice any issues.
Documentation
-------------
@@ -19,6 +80,35 @@ Documentation
removed from the tutorials section. It was moved to the
``pyramid_tutorials`` Github repository.
+Internals
+---------
+
+- The "view derivation" code is now factored into a set of classes rather
+ than a large number of standalone functions (a side effect of the
+ ``view_mapper`` refactoring).
+
+- The ``pyramid.renderer.RendererHelper`` class has grown a ``render_view``
+ method, which is used by the default view mapper (a side effect of the
+ ``view_mapper`` refactoring).
+
+- The object passed as ``renderer`` to the "view deriver" is now an instance
+ of ``pyramid.renderers.RendererHelper`` rather than a dictionary (a side
+ effect of ``view_mapper`` refactoring).
+
+- The class used as the "page template" in ``pyramid.chameleon_text`` was
+ removed, in preference to using a Chameleon-inbuilt version.
+
+- A view callable wrapper registered in the registry now contains an
+ ``__original_view__`` attribute which references the original view callable
+ (or class).
+
+- The (non-API) method of all internal authentication policy implementations
+ previously named ``_get_userid`` is now named ``unauthenticated_userid``,
+ promoted to an API method. If you were overriding this method, you'll now
+ need to override it as ``unauthenticated_userid`` instead.
+
+- Remove (non-API) function of config.py named _map_view.
+
1.0a8 (2010-12-27)
==================
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index b48e769a1..7b0364b6d 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -90,7 +90,7 @@ Licensing Exceptions
Code committed within the ``docs/`` subdirectory of the Pyramid source
control repository and "docstrings" which appear in the documentation
-generated by runnning "make" within this directory is licensed under the
+generated by running "make" within this directory is licensed under the
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States
License (http://creativecommons.org/licenses/by-nc-sa/3.0/us/).
@@ -117,3 +117,6 @@ Contributors
- Casey Duncan, 2010/12/27
+- Rob Miller, 2010/12/28
+
+- Marius Gedminas, 2010/12/31
diff --git a/TODO.txt b/TODO.txt
index ada3c4c2a..e7ac216e7 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -11,12 +11,30 @@ Must-Have (before 1.0)
- Re-make testing.setUp() and testing.tearDown() the canonical APIs for test
configuration.
-- ``decorator=`` parameter to view_config. This would replace the existing
- _map_view "decorator" if it existed (Rob needs).
+- Document ``decorator=`` and ``view_mapper`` parameters to add_view and
+ ``@view_config``.
+
+- Allow ``decorator=`` and ``view_mapper=`` to be passed via ZCML.
+
+- Document ``Configurator.set_view_mapper``.
+
+- Document ``__view_mapper__`` attribute for view callable view mapper
+ preference.
+
+- ZCML directive for view mapper (or just use "utility", but it's not eager).
+
+- Decide whether ``self.decorated_view(view)`` is in the right place in the
+ view deriver chain.
Should-Have
-----------
+- ``current_route_url`` function. https://gist.github.com/762842
+
+- Convert paster template and tutorial HTML templates to use
+ ``request.static_url('{{package}}:static/foo.css')`` rather than
+ ``${request.application_url}/static/foo.css``.
+
- Add notes about renderer response attrs to request docs.
- Add an example of using a cascade to serve static assets from the root.
diff --git a/docs/api/authentication.rst b/docs/api/authentication.rst
index 54db77417..a6d4c1e18 100644
--- a/docs/api/authentication.rst
+++ b/docs/api/authentication.rst
@@ -3,6 +3,9 @@
:mod:`pyramid.authentication`
--------------------------------
+Authentication Policies
+~~~~~~~~~~~~~~~~~~~~~~~
+
.. automodule:: pyramid.authentication
.. autoclass:: AuthTktAuthenticationPolicy
@@ -11,3 +14,10 @@
.. autoclass:: RemoteUserAuthenticationPolicy
+Helper Classes
+~~~~~~~~~~~~~~
+
+ .. autoclass:: AuthTktCookieHelper
+
+
+
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index b3c14e5f7..3ce926230 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -35,3 +35,7 @@ Other Interfaces
.. autointerface:: ITemplateRenderer
+ .. autointerface:: IViewMapperFactory
+
+ .. autointerface:: IViewMapper
+
diff --git a/docs/api/security.rst b/docs/api/security.rst
index 4acf5fe4d..de249355d 100644
--- a/docs/api/security.rst
+++ b/docs/api/security.rst
@@ -10,6 +10,8 @@ Authentication API Functions
.. autofunction:: authenticated_userid
+.. autofunction:: unauthenticated_userid
+
.. autofunction:: effective_principals
.. autofunction:: forget
diff --git a/docs/designdefense.rst b/docs/designdefense.rst
index 53b95b9d0..df14fb440 100644
--- a/docs/designdefense.rst
+++ b/docs/designdefense.rst
@@ -895,9 +895,9 @@ Pyramid Applications are Extensible; I Don't Believe In Application Extensibilit
Any :app:`Pyramid` application written obeying certain constraints is
*extensible*. This feature is discussed in the :app:`Pyramid` documentation
-chapters named :ref:`extending_chapter` and :ref:`advconf_narr`. It is made
-possible by the use of the :term:`Zope Component Architecture` and within
-:app:`Pyramid`.
+chapters named :ref:`extending_chapter` and :ref:`advconfig_narr`. It is
+made possible by the use of the :term:`Zope Component Architecture` and
+within :app:`Pyramid`.
"Extensible", in this context, means:
@@ -1018,7 +1018,7 @@ Challenge
:app:`Pyramid` performs automatic authorization checks only at :term:`view`
execution time. Zope 3 wraps context objects with a `security proxy
-<http://wiki.zope.org/zope3/WhatAreSecurityProxies>`, which causes Zope 3 to
+<http://wiki.zope.org/zope3/WhatAreSecurityProxies>`_, which causes Zope 3 to
do also security checks during attribute access. I like this, because it
means:
@@ -1604,10 +1604,10 @@ If you can understand this hello world program, you can use Pyramid:
app = config.make_wsgi_app()
serve(app)
-Pyramid has ~ 650 of documentation (printed), covering topics from the very
-basic to the most advanced. *Nothing* is left undocumented, quite literally.
-It also has an *awesome*, very helpful community. Visit the #repoze and/or
-#pylons IRC channels on freenode.net and see.
+Pyramid has ~ 650 pages of documentation (printed), covering topics from the
+very basic to the most advanced. *Nothing* is left undocumented, quite
+literally. It also has an *awesome*, very helpful community. Visit the
+#repoze and/or #pylons IRC channels on freenode.net and see.
Hate Zope
+++++++++
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 49d273197..4d0c53d60 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -850,6 +850,11 @@ Glossary
WSGI middleware which can display debuggable traceback information in
the browser when an exception is raised by a Pyramid application. See
http://pypi.python.org/pypi/WebError .
-
+ view mapper
+
+ A view mapper is a class which implements the
+ :class:`pyramid.interfaces.IViewMapperFactory` interface, which performs
+ view argument and return value mapping. This is a plug point for
+ extension builders, not normally used by "civilians".
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 55a2711f3..5e84a4fa7 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -910,6 +910,8 @@ example.
See :ref:`testing_chapter` for more information about writing :app:`Pyramid`
unit tests.
+.. _modifying_package_structure:
+
Modifying Package Structure
----------------------------
@@ -958,12 +960,14 @@ To this:
.. code-block:: python
:linenos:
- config.add_view('myproject.views.blogs.my_view',
+ config.add_view('myproject.views.blog.my_view',
renderer='myproject:templates/mytemplate.pt')
You can then continue to add files to the ``views`` directory, and refer to
views or handler classes/functions within those files via the dotted name
-passed as the first argument to ``add_view``. For example:
+passed as the first argument to ``add_view``. For example, if you added a
+file named ``anothermodule.py`` to the ``views`` subdirectory, and added a
+view callable named ``my_view`` to it:
.. code-block:: python
:linenos:
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index 437b823e9..7ef8e1923 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -628,7 +628,7 @@ application's configuration section, e.g.:
.. code-block:: ini
:linenos:
- [app:main]
+ [app:MyProject]
use = egg:MyProject#app
debug_templates = true
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index 86d725bcf..bf08b519a 100644
--- a/pyramid/authentication.py
+++ b/pyramid/authentication.py
@@ -17,7 +17,7 @@ from pyramid.security import Everyone
class CallbackAuthenticationPolicy(object):
""" Abstract class """
def authenticated_userid(self, request):
- userid = self._get_userid(request)
+ userid = self.unauthenticated_userid(request)
if userid is None:
return None
if self.callback is None:
@@ -27,7 +27,7 @@ class CallbackAuthenticationPolicy(object):
def effective_principals(self, request):
effective_principals = [Everyone]
- userid = self._get_userid(request)
+ userid = self.unauthenticated_userid(request)
if userid is None:
return effective_principals
if self.callback is None:
@@ -89,6 +89,12 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
if self.callback(identity, request) is not None: # is not None!
return identity['repoze.who.userid']
+ def unauthenticated_userid(self, request):
+ identity = self._get_identity(request)
+ if identity is None:
+ return None
+ return identity['repoze.who.userid']
+
def effective_principals(self, request):
effective_principals = [Everyone]
identity = self._get_identity(request)
@@ -147,7 +153,7 @@ class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
self.environ_key = environ_key
self.callback = callback
- def _get_userid(self, request):
+ def unauthenticated_userid(self, request):
return request.environ.get(self.environ_key)
def remember(self, request, principal, **kw):
@@ -264,7 +270,7 @@ class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
)
self.callback = callback
- def _get_userid(self, request):
+ def unauthenticated_userid(self, request):
result = self.cookie.identify(request)
if result:
return result['userid']
@@ -285,6 +291,12 @@ def b64decode(v):
EXPIRE = object()
class AuthTktCookieHelper(object):
+ """
+ A helper class for use in third-party authentication policy
+ implementations. See
+ :class:`pyramid.authentication.AuthTktAuthenticationPolicy' for the
+ meanings of the constructor arguments.
+ """
auth_tkt = auth_tkt # for tests
now = None # for tests
@@ -356,6 +368,8 @@ class AuthTktCookieHelper(object):
return cookies
def identify(self, request):
+ """ Return a dictionary with authentication information, or ``None``
+ if no valid auth_tkt is attached to ``request``"""
environ = request.environ
cookies = get_cookies(environ)
cookie = cookies.get(self.cookie_name)
@@ -411,11 +425,14 @@ class AuthTktCookieHelper(object):
return identity
def forget(self, request):
- # return a set of expires Set-Cookie headers
+ """ Return a set of expires Set-Cookie headers, which will destroy
+ any existing auth_tkt cookie when attached to a response"""
environ = request.environ
return self._get_cookies(environ, '', max_age=EXPIRE)
def remember(self, request, userid, max_age=None):
+ """ Return a set of Set-Cookie headers; when set into a response,
+ these headers will represent a valid authentication ticket."""
max_age = max_age or self.max_age
environ = request.environ
diff --git a/pyramid/chameleon_text.py b/pyramid/chameleon_text.py
index 32896b8e9..b687ecda9 100644
--- a/pyramid/chameleon_text.py
+++ b/pyramid/chameleon_text.py
@@ -4,39 +4,22 @@ from zope.deprecation import deprecated
from zope.interface import implements
try:
- from chameleon.core.template import TemplateFile
- TemplateFile # prevent pyflakes complaining about a redefinition below
+ from chameleon.zpt.template import PageTextTemplateFile
+ # prevent pyflakes complaining about a redefinition below
+ PageTextTemplateFile
except ImportError: # pragma: no cover
exc_class, exc, tb = sys.exc_info()
# Chameleon doesn't work on non-CPython platforms
- class TemplateFile(object):
+ class PageTextTemplateFile(object):
def __init__(self, *arg, **kw):
raise ImportError, exc, tb
-try:
- from chameleon.zpt.language import Parser
- Parser # prevent pyflakes complaining about a redefinition below
-except ImportError: # pragma: no cover
- # Chameleon doesn't work on non-CPython platforms
- class Parser(object):
- pass
-
from pyramid.interfaces import ITemplateRenderer
from pyramid.decorator import reify
from pyramid import renderers
from pyramid.path import caller_package
-class TextTemplateFile(TemplateFile):
- default_parser = Parser()
-
- def __init__(self, filename, parser=None, format='text', doctype=None,
- **kwargs):
- if parser is None:
- parser = self.default_parser
- super(TextTemplateFile, self).__init__(filename, parser, format,
- doctype, **kwargs)
-
def renderer_factory(info):
return renderers.template_renderer_factory(info, TextTemplateRenderer)
@@ -51,10 +34,10 @@ class TextTemplateRenderer(object):
if sys.platform.startswith('java'): # pragma: no cover
raise RuntimeError(
'Chameleon templates are not compatible with Jython')
- return TextTemplateFile(self.path,
- auto_reload=self.lookup.auto_reload,
- debug=self.lookup.debug,
- translate=self.lookup.translate)
+ return PageTextTemplateFile(self.path,
+ auto_reload=self.lookup.auto_reload,
+ debug=self.lookup.debug,
+ translate=self.lookup.translate)
def implementation(self):
return self.template
diff --git a/pyramid/config.py b/pyramid/config.py
index f6b4a2112..338be2a98 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -45,6 +45,7 @@ from pyramid.interfaces import ITranslationDirectories
from pyramid.interfaces import ITraverser
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
+from pyramid.interfaces import IViewMapperFactory
try:
from pyramid import chameleon_text
@@ -245,7 +246,13 @@ class Configurator(object):
``autocommit`` is ``True``. If a conflict is detected a
``ConfigurationConflictError`` will be raised. Calling
:meth:`pyramid.config.Configurator.make_wsgi_app` always implies a final
- commit."""
+ commit.
+
+ If ``default_view_mapper`` is passed, it will be used as the default
+ view mapper factory for view configurations that don't otherwise specify
+ one (see :class:`pyramid.interfaces.IViewMapperFactory`).
+ If a default_view_mapper is not passed, a superdefault view mapper will
+ be used. """
manager = manager # for testing injection
venusian = venusian # for testing injection
@@ -266,6 +273,7 @@ class Configurator(object):
renderer_globals_factory=None,
default_permission=None,
session_factory=None,
+ default_view_mapper=None,
autocommit=False,
):
if package is None:
@@ -291,6 +299,7 @@ class Configurator(object):
renderer_globals_factory=renderer_globals_factory,
default_permission=default_permission,
session_factory=session_factory,
+ default_view_mapper=default_view_mapper,
)
def _set_settings(self, mapping):
@@ -338,30 +347,39 @@ class Configurator(object):
def _split_spec(self, path_or_spec):
return resolve_asset_spec(path_or_spec, self.package_name)
+ # b/w compat
def _derive_view(self, view, permission=None, predicates=(),
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}
+ phash=DEFAULT_PHASH, decorator=None,
+ view_mapper=None):
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, self.registry, attr, renderer)
- owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname)
- secured_view = _secure_view(owrapped_view, permission,
- authn_policy, authz_policy)
- debug_view = _authdebug_view(secured_view, permission,
- authn_policy, authz_policy, settings,
- logger)
- predicated_view = _predicate_wrap(debug_view, predicates)
- derived_view = _attr_wrap(predicated_view, accept, order, phash)
- return derived_view
+ view_mapper = self.maybe_dotted(view_mapper)
+ if isinstance(renderer, basestring):
+ renderer = RendererHelper(name=renderer, package=self.package,
+ registry = self.registry)
+ if renderer is None:
+ # use default renderer if one exists
+ if self.registry.queryUtility(IRendererFactory) is not None:
+ renderer = RendererHelper(name=None,
+ package=self.package,
+ registry=self.registry)
+
+ deriver = ViewDeriver(registry=self.registry,
+ permission=permission,
+ predicates=predicates,
+ attr=attr,
+ renderer=renderer,
+ wrapper_viewname=wrapper_viewname,
+ viewname=viewname,
+ accept=accept,
+ order=order,
+ phash=phash,
+ package=self.package,
+ view_mapper=view_mapper,
+ decorator=decorator)
+
+ return deriver(view)
def _override(self, package, path, override_package, override_prefix,
PackageOverrides=PackageOverrides):
@@ -591,9 +609,8 @@ class Configurator(object):
authentication_policy=None, authorization_policy=None,
renderers=DEFAULT_RENDERERS, debug_logger=None,
locale_negotiator=None, request_factory=None,
- renderer_globals_factory=None,
- default_permission=None,
- session_factory=None):
+ renderer_globals_factory=None, default_permission=None,
+ session_factory=None, default_view_mapper=None):
""" When you pass a non-``None`` ``registry`` argument to the
:term:`Configurator` constructor, no initial 'setup' is performed
against the registry. This is because the registry you pass in may
@@ -639,7 +656,13 @@ class Configurator(object):
self.set_default_permission(default_permission)
if session_factory is not None:
self.set_session_factory(session_factory)
+ # commit before adding default_view_mapper, as the
+ # default_exceptionresponse_view above requires the superdefault view
+ # mapper
self.commit()
+ if default_view_mapper is not None:
+ self.set_view_mapper(default_view_mapper)
+ self.commit()
# getSiteManager is a unit testing dep injection
def hook_zca(self, getSiteManager=None):
@@ -757,9 +780,6 @@ class Configurator(object):
a :term:`response` object. If a ``renderer`` argument is not
supplied, the user-supplied view must itself return a
:term:`response` object. """
-
- if renderer is not None and not isinstance(renderer, dict):
- renderer = {'name':renderer, 'package':self.package}
return self._derive_view(view, attr=attr, renderer=renderer)
@action_method
@@ -941,6 +961,20 @@ class Configurator(object):
pattern = route.pattern
+ action_decorator = getattr(handler, '__action_decorator__', None)
+ if action_decorator is not None:
+ if hasattr(action_decorator, 'im_self'):
+ # instance methods have an im_self == None
+ # classmethods have an im_self == cls
+ # staticmethods have no im_self
+ # instances have no im_self
+ if action_decorator.im_self is not handler:
+ raise ConfigurationError(
+ 'The "__action_decorator__" attribute of a handler '
+ 'must not be an instance method (must be a '
+ 'staticmethod, classmethod, function, or an instance '
+ 'which is a callable')
+
path_has_action = ':action' in pattern or '{action}' in pattern
if action and path_has_action:
@@ -970,7 +1004,8 @@ class Configurator(object):
preds.append(ActionPredicate(action))
view_args['custom_predicates'] = preds
self.add_view(view=handler, attr=method_name,
- route_name=route_name, **view_args)
+ route_name=route_name,
+ decorator=action_decorator, **view_args)
else:
method_name = action
if method_name is None:
@@ -993,14 +1028,15 @@ class Configurator(object):
view_args = expose_config.copy()
del view_args['name']
self.add_view(view=handler, attr=meth_name,
- route_name=route_name, **view_args)
+ route_name=route_name,
+ decorator=action_decorator, **view_args)
# Now register the method itself
method = getattr(handler, method_name, None)
configs = getattr(method, '__exposed__', [{}])
for expose_config in configs:
self.add_view(view=handler, attr=action, route_name=route_name,
- **expose_config)
+ decorator=action_decorator, **expose_config)
return route
@@ -1010,7 +1046,7 @@ class Configurator(object):
request_param=None, containment=None, attr=None,
renderer=None, wrapper=None, xhr=False, accept=None,
header=None, path_info=None, custom_predicates=(),
- context=None):
+ context=None, decorator=None, view_mapper=None):
""" Add a :term:`view configuration` to the current
configuration state. Arguments to ``add_view`` are broken
down below into *predicate* arguments and *non-predicate*
@@ -1119,6 +1155,15 @@ class Configurator(object):
view is the same context and request of the inner view. If
this attribute is unspecified, no view wrapping is done.
+ decorator
+
+ A function which will be used to decorate the registered
+ :term:`view callable`. The decorator function will be called with
+ the view callable as a single argument. The view callable it is
+ passed will accept ``(context, request)`. The decorator must
+ return a replacement view callable which also accepts ``(context,
+ request)``.
+
Predicate Arguments
name
@@ -1255,11 +1300,22 @@ class Configurator(object):
the context and/or the request. If all callables return
``True``, the associated view callable will be considered
viable for a given request.
+
+ view_mapper
+
+ A Python object or :term:`dotted Python name` which refers to a
+ :term:`view mapper`, or ``None``. By default it is ``None``, which
+ indicates that the view should use the default view mapper. This
+ plug-point is useful for Pyramid extension developers, but it's not
+ very useful for 'civilians' who are just developing stock Pyramid
+ applications. Pay no attention to the man behind the curtain.
+
"""
view = self.maybe_dotted(view)
context = self.maybe_dotted(context)
for_ = self.maybe_dotted(for_)
containment = self.maybe_dotted(containment)
+ view_mapper = self.maybe_dotted(view_mapper)
if not view:
if renderer:
@@ -1293,6 +1349,7 @@ class Configurator(object):
renderer=renderer, wrapper=wrapper, xhr=xhr, accept=accept,
header=header, path_info=path_info,
custom_predicates=custom_predicates, context=context,
+ view_mapper = view_mapper,
)
view_info = deferred_views.setdefault(route_name, [])
view_info.append(info)
@@ -1304,9 +1361,6 @@ class Configurator(object):
containment=containment, request_type=request_type,
custom=custom_predicates)
- if renderer is not None and not isinstance(renderer, dict):
- renderer = {'name':renderer, 'package':self.package}
-
if context is None:
context = for_
@@ -1316,16 +1370,37 @@ class Configurator(object):
if not IInterface.providedBy(r_context):
r_context = implementedBy(r_context)
- def register(permission=permission):
+ if isinstance(renderer, basestring):
+ renderer = RendererHelper(name=renderer, package=self.package,
+ registry = self.registry)
+
+ def register(permission=permission, renderer=renderer):
+ if renderer is None:
+ # use default renderer if one exists
+ if self.registry.queryUtility(IRendererFactory) is not None:
+ renderer = RendererHelper(name=None,
+ package=self.package,
+ registry=self.registry)
if permission is None:
# intent: will be None if no default permission is registered
permission = self.registry.queryUtility(IDefaultPermission)
- # NO_PERMISSION_REQUIRED handled by _secure_view
- derived_view = self._derive_view(view, permission, predicates, attr,
- renderer, wrapper, name, accept,
- order, phash)
+ # __no_permission_required__ handled by _secure_view
+ deriver = ViewDeriver(registry=self.registry,
+ permission=permission,
+ predicates=predicates,
+ attr=attr,
+ renderer=renderer,
+ wrapper_viewname=wrapper,
+ viewname=name,
+ accept=accept,
+ order=order,
+ phash=phash,
+ package=self.package,
+ view_mapper=view_mapper,
+ decorator=decorator)
+ derived_view = deriver(view)
registered = self.registry.adapters.registered
@@ -1959,8 +2034,9 @@ class Configurator(object):
The ``wrapper`` argument should be the name of another view
which will wrap this view when rendered (see the ``add_view``
method's ``wrapper`` argument for a description)."""
- if renderer is not None and not isinstance(renderer, dict):
- renderer = {'name':renderer, 'package':self.package}
+ if isinstance(renderer, basestring):
+ renderer = RendererHelper(name=renderer, package=self.package,
+ registry = self.registry)
view = self._derive_view(view, attr=attr, renderer=renderer)
def bwcompat_view(context, request):
context = getattr(request, 'context', None)
@@ -1998,8 +2074,9 @@ class Configurator(object):
which will wrap this view when rendered (see the ``add_view``
method's ``wrapper`` argument for a description).
"""
- if renderer is not None and not isinstance(renderer, dict):
- renderer = {'name':renderer, 'package':self.package}
+ if isinstance(renderer, basestring):
+ renderer = RendererHelper(name=renderer, package=self.package,
+ registry=self.registry)
view = self._derive_view(view, attr=attr, renderer=renderer)
def bwcompat_view(context, request):
context = getattr(request, 'context', None)
@@ -2105,6 +2182,29 @@ class Configurator(object):
self.action(IDefaultPermission, None)
@action_method
+ def set_view_mapper(self, mapper):
+ """
+ Setting a :term:`view mapper` makes it possible to make use of
+ :term:`view callable` objects which implement different call
+ signatures than the ones supported by :app:`Pyramid` as described in
+ its narrative documentation.
+
+ The ``mapper`` should argument be an object implementing
+ :class:`pyramid.interfaces.IViewMapperFactory` or a :term:`dotted
+ Python name` to such an object.
+
+ The provided ``mapper`` will become the default view mapper to be
+ used by all subsequent :term:`view configuration` registrations, as
+ if you had passed a ``default_view_mapper`` argument to the
+ :class:`pyramid.config.Configurator` constructor.
+
+ See also :ref:`using_an_alternate_view_mapper`.
+ """
+ mapper = self.maybe_dotted(mapper)
+ self.registry.registerUtility(mapper, IViewMapperFactory)
+ self.action(IViewMapperFactory, None)
+
+ @action_method
def set_session_factory(self, session_factory):
"""
Configure the application with a :term:`session factory`. If
@@ -2158,11 +2258,6 @@ class Configurator(object):
# same function once for each added translation directory,
# which does too much work, but has the same effect.
- def translator(msg):
- request = get_current_request()
- localizer = get_localizer(request)
- return localizer.translate(msg)
-
ctranslate = ChameleonTranslate(translator)
self.registry.registerUtility(ctranslate, IChameleonTranslate)
@@ -2633,52 +2728,327 @@ class MultiView(object):
continue
raise PredicateMismatch(self.name)
-def decorate_view(wrapped_view, original_view):
- if wrapped_view is original_view:
- return False
- wrapped_view.__module__ = original_view.__module__
- wrapped_view.__doc__ = original_view.__doc__
+def wraps_view(wrapped):
+ def inner(self, view):
+ wrapped_view = wrapped(self, view)
+ return preserve_view_attrs(view, wrapped_view)
+ return inner
+
+def preserve_view_attrs(view, wrapped_view):
+ if wrapped_view is view:
+ return view
+ original_view = getattr(view, '__original_view__', None)
+ if original_view is None:
+ original_view = view
+ wrapped_view.__original_view__ = original_view
+ wrapped_view.__module__ = view.__module__
+ wrapped_view.__doc__ = view.__doc__
try:
- wrapped_view.__name__ = original_view.__name__
+ wrapped_view.__name__ = view.__name__
except AttributeError:
- wrapped_view.__name__ = repr(original_view)
+ wrapped_view.__name__ = repr(view)
try:
- wrapped_view.__permitted__ = original_view.__permitted__
+ wrapped_view.__permitted__ = view.__permitted__
except AttributeError:
pass
try:
- wrapped_view.__call_permissive__ = original_view.__call_permissive__
+ wrapped_view.__call_permissive__ = view.__call_permissive__
except AttributeError:
pass
try:
- wrapped_view.__predicated__ = original_view.__predicated__
+ wrapped_view.__predicated__ = view.__predicated__
except AttributeError:
pass
try:
- wrapped_view.__accept__ = original_view.__accept__
+ wrapped_view.__accept__ = view.__accept__
except AttributeError:
pass
try:
- wrapped_view.__order__ = original_view.__order__
+ wrapped_view.__order__ = view.__order__
except AttributeError:
pass
- return True
+ return wrapped_view
+
+class ViewDeriver(object):
+ def __init__(self, **kw):
+ self.kw = kw
+ self.registry = kw['registry']
+ self.authn_policy = self.registry.queryUtility(
+ IAuthenticationPolicy)
+ self.authz_policy = self.registry.queryUtility(
+ IAuthorizationPolicy)
+ self.logger = self.registry.queryUtility(IDebugLogger)
+
+ def __call__(self, view):
+ return self.attr_wrapped_view(
+ self.predicated_view(
+ self.authdebug_view(
+ self.secured_view(
+ self.owrapped_view(
+ self.decorated_view(
+ self.rendered_view(
+ self.mapped_view(view))))))))
+
+ @wraps_view
+ def mapped_view(self, view):
+ mapper = self.kw.get('view_mapper')
+ if mapper is None:
+ mapper = getattr(view, '__view_mapper__', None)
+ if mapper is None:
+ mapper = self.registry.queryUtility(IViewMapperFactory)
+ if mapper is None:
+ mapper = DefaultViewMapper
+
+ mapped_view = mapper(**self.kw)(view)
+ return mapped_view
+
+ @wraps_view
+ def owrapped_view(self, view):
+ wrapper_viewname = self.kw.get('wrapper_viewname')
+ viewname = self.kw.get('viewname')
+ if not wrapper_viewname:
+ return view
+ def _owrapped_view(context, request):
+ response = view(context, request)
+ request.wrapped_response = response
+ request.wrapped_body = response.body
+ request.wrapped_view = view
+ wrapped_response = render_view_to_response(context, request,
+ wrapper_viewname)
+ if wrapped_response is None:
+ raise ValueError(
+ 'No wrapper view named %r found when executing view '
+ 'named %r' % (wrapper_viewname, viewname))
+ return wrapped_response
+ return _owrapped_view
+
+ @wraps_view
+ def secured_view(self, view):
+ permission = self.kw.get('permission')
+ if permission == '__no_permission_required__':
+ # allow views registered within configurations that have a
+ # default permission to explicitly override the default
+ # permission, replacing it with no permission at all
+ permission = None
+
+ wrapped_view = view
+ if self.authn_policy and self.authz_policy and (permission is not None):
+ def _secured_view(context, request):
+ principals = self.authn_policy.effective_principals(request)
+ if self.authz_policy.permits(context, principals, permission):
+ return view(context, request)
+ msg = getattr(request, 'authdebug_message',
+ 'Unauthorized: %s failed permission check' % view)
+ raise Forbidden(msg)
+ _secured_view.__call_permissive__ = view
+ def _permitted(context, request):
+ principals = self.authn_policy.effective_principals(request)
+ return self.authz_policy.permits(context, principals,
+ permission)
+ _secured_view.__permitted__ = _permitted
+ wrapped_view = _secured_view
+
+ return wrapped_view
+
+ @wraps_view
+ def authdebug_view(self, view):
+ wrapped_view = view
+ settings = self.registry.settings
+ permission = self.kw.get('permission')
+ if settings and settings.get('debug_authorization', False):
+ def _authdebug_view(context, request):
+ view_name = getattr(request, 'view_name', None)
+
+ if self.authn_policy and self.authz_policy:
+ if permission is None:
+ msg = 'Allowed (no permission registered)'
+ else:
+ principals = self.authn_policy.effective_principals(
+ request)
+ msg = str(self.authz_policy.permits(context, principals,
+ permission))
+ else:
+ msg = 'Allowed (no authorization policy in use)'
+
+ view_name = getattr(request, 'view_name', None)
+ url = getattr(request, 'url', None)
+ msg = ('debug_authorization of url %s (view name %r against '
+ 'context %r): %s' % (url, view_name, context, msg))
+ self.logger and self.logger.debug(msg)
+ if request is not None:
+ request.authdebug_message = msg
+ return view(context, request)
+
+ wrapped_view = _authdebug_view
+
+ return wrapped_view
-def requestonly(class_or_callable, attr=None):
- """ Return true of the class or callable accepts only a request argument,
- as opposed to something that accepts context, request """
+ @wraps_view
+ def predicated_view(self, view):
+ predicates = self.kw.get('predicates', ())
+ if not predicates:
+ return view
+ def predicate_wrapper(context, request):
+ if all((predicate(context, request) for predicate in predicates)):
+ return view(context, request)
+ raise PredicateMismatch('predicate mismatch for view %s' % view)
+ def checker(context, request):
+ return all((predicate(context, request) for predicate in
+ predicates))
+ predicate_wrapper.__predicated__ = checker
+ return predicate_wrapper
+
+ @wraps_view
+ def attr_wrapped_view(self, view):
+ kw = self.kw
+ accept, order, phash = (kw.get('accept', None),
+ kw.get('order', MAX_ORDER),
+ kw.get('phash', DEFAULT_PHASH))
+ # this is a little silly but we don't want to decorate the original
+ # function with attributes that indicate accept, order, and phash,
+ # so we use a wrapper
+ if ( (accept is None) and (order == MAX_ORDER) and
+ (phash == DEFAULT_PHASH) ):
+ return view # defaults
+ def attr_view(context, request):
+ return view(context, request)
+ attr_view.__accept__ = accept
+ attr_view.__order__ = order
+ attr_view.__phash__ = phash
+ return attr_view
+
+ @wraps_view
+ def rendered_view(self, view):
+ wrapped_view = view
+ static_renderer = self.kw.get('renderer')
+ if static_renderer is None:
+ # register a default renderer if you want super-dynamic
+ # rendering. registering a default renderer will also allow
+ # override_renderer to work if a renderer is left unspecified for
+ # a view registration.
+ return view
+
+ def _rendered_view(context, request):
+ renderer = static_renderer
+ response = wrapped_view(context, request)
+ if not is_response(response):
+ attrs = getattr(request, '__dict__', {})
+ if 'override_renderer' in attrs:
+ # renderer overridden by newrequest event or other
+ renderer_name = attrs.pop('override_renderer')
+ renderer = RendererHelper(name=renderer_name,
+ package=self.kw.get('package'),
+ registry = self.kw['registry'])
+ if '__view__' in attrs:
+ view_inst = attrs.pop('__view__')
+ else:
+ view_inst = getattr(wrapped_view, '__original_view__',
+ wrapped_view)
+ return renderer.render_view(request, response, view_inst,
+ context)
+ return response
+
+ return _rendered_view
+
+ @wraps_view
+ def decorated_view(self, view):
+ decorator = self.kw.get('decorator')
+ if decorator is None:
+ return view
+ return decorator(view)
+
+class DefaultViewMapper(object):
+ implements(IViewMapperFactory)
+ def __init__(self, **kw):
+ self.attr = kw.get('attr')
+
+ def __call__(self, view):
+ if inspect.isclass(view):
+ view = self.map_class(view)
+ else:
+ view = self.map_nonclass(view)
+ return view
+
+ def map_class(self, view):
+ ronly = requestonly(view, self.attr)
+ if ronly:
+ mapped_view = self.map_class_requestonly(view)
+ else:
+ mapped_view = self.map_class_native(view)
+ return mapped_view
+
+ def map_nonclass(self, view):
+ # We do more work here than appears necessary to avoid wrapping the
+ # view unless it actually requires wrapping (to avoid function call
+ # overhead).
+ mapped_view = view
+ ronly = requestonly(view, self.attr)
+ if ronly:
+ mapped_view = self.map_nonclass_requestonly(view)
+ elif self.attr:
+ mapped_view = self.map_nonclass_attr(view)
+ return mapped_view
+
+ def map_class_requestonly(self, view):
+ # its a class that has an __init__ which only accepts request
+ attr = self.attr
+ def _class_requestonly_view(context, request):
+ inst = view(request)
+ request.__view__ = inst
+ if attr is None:
+ response = inst()
+ else:
+ response = getattr(inst, attr)()
+ return response
+ return _class_requestonly_view
+
+ def map_class_native(self, view):
+ # its a class that has an __init__ which accepts both context and
+ # request
+ attr = self.attr
+ def _class_view(context, request):
+ inst = view(context, request)
+ request.__view__ = inst
+ if attr is None:
+ response = inst()
+ else:
+ response = getattr(inst, attr)()
+ return response
+ return _class_view
+
+ def map_nonclass_requestonly(self, view):
+ # its a function that has a __call__ which accepts only a single
+ # request argument
+ attr = self.attr
+ def _requestonly_view(context, request):
+ if attr is None:
+ response = view(request)
+ else:
+ response = getattr(view, attr)(request)
+ return response
+ return _requestonly_view
+
+ def map_nonclass_attr(self, view):
+ # its a function that has a __call__ which accepts both context and
+ # request, but still has an attr
+ def _attr_view(context, request):
+ response = getattr(view, self.attr)(context, request)
+ return response
+ return _attr_view
+
+def requestonly(view, attr=None):
if attr is None:
attr = '__call__'
- if inspect.isfunction(class_or_callable):
- fn = class_or_callable
- elif inspect.isclass(class_or_callable):
+ if inspect.isfunction(view):
+ fn = view
+ elif inspect.isclass(view):
try:
- fn = class_or_callable.__init__
+ fn = view.__init__
except AttributeError:
return False
else:
try:
- fn = getattr(class_or_callable, attr)
+ fn = getattr(view, attr)
except AttributeError:
return False
@@ -2707,242 +3077,6 @@ def requestonly(class_or_callable, attr=None):
return False
-def is_response(ob):
- if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and
- hasattr(ob, 'status') ):
- return True
- return False
-
-def _map_view(view, registry, attr=None, renderer=None):
- wrapped_view = view
-
- helper = None
-
- if renderer is not None:
- helper = RendererHelper(renderer['name'],
- package=renderer['package'],
- registry=registry)
-
- if inspect.isclass(view):
- # If the object we've located is a class, turn it into a
- # function that operates like a Zope view (when it's invoked,
- # construct an instance using 'context' and 'request' as
- # position arguments, then immediately invoke the __call__
- # method of the instance with no arguments; __call__ should
- # return an IResponse).
- if requestonly(view, attr):
- # its __init__ accepts only a single request argument,
- # instead of both context and request
- def _class_requestonly_view(context, request):
- inst = view(request)
- if attr is None:
- response = inst()
- else:
- response = getattr(inst, attr)()
- if helper is not None:
- if not is_response(response):
- system = {
- 'view':inst,
- 'renderer_name':renderer['name'], # b/c
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _class_requestonly_view
- else:
- # its __init__ accepts both context and request
- def _class_view(context, request):
- inst = view(context, request)
- if attr is None:
- response = inst()
- else:
- response = getattr(inst, attr)()
- if helper is not None:
- if not is_response(response):
- system = {'view':inst,
- 'renderer_name':renderer['name'], # b/c
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _class_view
-
- elif requestonly(view, attr):
- # its __call__ accepts only a single request argument,
- # instead of both context and request
- def _requestonly_view(context, request):
- if attr is None:
- response = view(request)
- else:
- response = getattr(view, attr)(request)
-
- if helper is not None:
- if not is_response(response):
- system = {
- 'view':view,
- 'renderer_name':renderer['name'],
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _requestonly_view
-
- elif attr:
- def _attr_view(context, request):
- response = getattr(view, attr)(context, request)
- if helper is not None:
- if not is_response(response):
- system = {
- 'view':view,
- 'renderer_name':renderer['name'],
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _attr_view
-
- elif helper is not None:
- def _rendered_view(context, request):
- response = view(context, request)
- if not is_response(response):
- system = {
- 'view':view,
- 'renderer_name':renderer['name'], # b/c
- 'renderer_info':renderer,
- 'context':context,
- 'request':request
- }
- response = helper.render_to_response(response, system,
- request=request)
- return response
- wrapped_view = _rendered_view
-
- decorate_view(wrapped_view, view)
- return wrapped_view
-
-def _owrap_view(view, viewname, wrapper_viewname):
- if not wrapper_viewname:
- return view
- def _owrapped_view(context, request):
- response = view(context, request)
- request.wrapped_response = response
- request.wrapped_body = response.body
- request.wrapped_view = view
- wrapped_response = render_view_to_response(context, request,
- wrapper_viewname)
- if wrapped_response is None:
- raise ValueError(
- 'No wrapper view named %r found when executing view '
- 'named %r' % (wrapper_viewname, viewname))
- return wrapped_response
- decorate_view(_owrapped_view, view)
- return _owrapped_view
-
-def _predicate_wrap(view, predicates):
- if not predicates:
- return view
- def predicate_wrapper(context, request):
- if all((predicate(context, request) for predicate in predicates)):
- return view(context, request)
- raise PredicateMismatch('predicate mismatch for view %s' % view)
- def checker(context, request):
- return all((predicate(context, request) for predicate in
- predicates))
- predicate_wrapper.__predicated__ = checker
- decorate_view(predicate_wrapper, view)
- return predicate_wrapper
-
-def _secure_view(view, permission, authn_policy, authz_policy):
- if permission == '__no_permission_required__':
- # allow views registered within configurations that have a
- # default permission to explicitly override the default
- # permission, replacing it with no permission at all
- permission = None
-
- wrapped_view = view
- if authn_policy and authz_policy and (permission is not None):
- def _secured_view(context, request):
- principals = authn_policy.effective_principals(request)
- if authz_policy.permits(context, principals, permission):
- return view(context, request)
- msg = getattr(request, 'authdebug_message',
- 'Unauthorized: %s failed permission check' % view)
- raise Forbidden(msg)
- _secured_view.__call_permissive__ = view
- def _permitted(context, request):
- principals = authn_policy.effective_principals(request)
- return authz_policy.permits(context, principals, permission)
- _secured_view.__permitted__ = _permitted
- wrapped_view = _secured_view
- decorate_view(wrapped_view, view)
-
- return wrapped_view
-
-def _authdebug_view(view, permission, authn_policy, authz_policy, settings,
- logger):
- wrapped_view = view
- if settings and settings.get('debug_authorization', False):
- def _authdebug_view(context, request):
- view_name = getattr(request, 'view_name', None)
-
- if authn_policy and authz_policy:
- if permission is None:
- msg = 'Allowed (no permission registered)'
- else:
- principals = authn_policy.effective_principals(request)
- msg = str(authz_policy.permits(context, principals,
- permission))
- else:
- msg = 'Allowed (no authorization policy in use)'
-
- view_name = getattr(request, 'view_name', None)
- url = getattr(request, 'url', None)
- msg = ('debug_authorization of url %s (view name %r against '
- 'context %r): %s' % (url, view_name, context, msg))
- logger and logger.debug(msg)
- if request is not None:
- request.authdebug_message = msg
- return view(context, request)
-
- wrapped_view = _authdebug_view
- decorate_view(wrapped_view, view)
-
- return wrapped_view
-
-def _attr_wrap(view, accept, order, phash):
- # this is a little silly but we don't want to decorate the original
- # function with attributes that indicate accept, order, and phash,
- # so we use a wrapper
- if (accept is None) and (order == MAX_ORDER) and (phash == DEFAULT_PHASH):
- return view # defaults
- def attr_view(context, request):
- return view(context, request)
- attr_view.__accept__ = accept
- attr_view.__order__ = order
- attr_view.__phash__ = phash
- decorate_view(attr_view, view)
- return attr_view
-
-def isexception(o):
- if IInterface.providedBy(o):
- if IException.isEqualOrExtendedBy(o):
- return True
- return (
- isinstance(o, Exception) or
- (inspect.isclass(o) and (issubclass(o, Exception)))
- )
class ActionPredicate(object):
action_name = 'action'
@@ -2984,3 +3118,23 @@ class PyramidConfigurationMachine(ConfigurationMachine):
self._seen_files.add(spec)
return True
+def translator(msg):
+ request = get_current_request()
+ localizer = get_localizer(request)
+ return localizer.translate(msg)
+
+def is_response(ob):
+ if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and
+ hasattr(ob, 'status') ):
+ return True
+ return False
+
+def isexception(o):
+ if IInterface.providedBy(o):
+ if IException.isEqualOrExtendedBy(o):
+ return True
+ return (
+ isinstance(o, Exception) or
+ (inspect.isclass(o) and (issubclass(o, Exception)))
+ )
+
diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py
index 6d05f9475..f56910b53 100644
--- a/pyramid/httpexceptions.py
+++ b/pyramid/httpexceptions.py
@@ -3,6 +3,7 @@ from webob.exc import status_map
# Parent classes
from webob.exc import HTTPException
+from webob.exc import WSGIHTTPException
from webob.exc import HTTPOk
from webob.exc import HTTPRedirection
from webob.exc import HTTPError
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index d21976209..b109df77e 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -120,6 +120,27 @@ class ITemplateRenderer(IRenderer):
accepts arbitrary keyword arguments and returns a string or
unicode object """
+class IViewMapper(Interface):
+ def __call__(self, object):
+ """ Provided with an arbitrary object (a function, class, or
+ instance), returns a callable with the call signature ``(context,
+ request)``. The callable returned should itself return a Response
+ object. An IViewMapper is returned by
+ :class:`pyramid.interfaces.IViewMapperFactory`."""
+
+class IViewMapperFactory(Interface):
+ def __call__(self, **kw):
+ """
+ Return an object which implements
+ :class:`pyramid.interfaces.IViewMapper`. ``kw`` will be a dictionary
+ containing view-specific arguments, such as ``permission``,
+ ``predicates``, ``attr``, ``renderer``, and other items. An
+ IViewMapperFactory is used by
+ :meth:`pyramid.config.Configurator.add_view` to provide a plugpoint
+ to extension developers who want to modify potential view callable
+ invocation signatures and response values.
+ """
+
# internal interfaces
class IRequest(Interface):
@@ -134,8 +155,19 @@ class IRouteRequest(Interface):
class IAuthenticationPolicy(Interface):
""" An object representing a Pyramid authentication policy. """
def authenticated_userid(request):
- """ Return the authenticated userid or ``None`` if no
- authenticated userid can be found. """
+ """ Return the authenticated userid or ``None`` if no authenticated
+ userid can be found. This method of the policy should ensure that a
+ record exists in whatever persistent store is used related to the
+ user (the user should not have been deleted); if a record associated
+ with the current id does not exist in a persistent store, it should
+ return ``None``."""
+
+ def unauthenticated_userid(request):
+ """ Return the *unauthenticated* userid. This method performs the
+ same duty as ``authenticated_userid`` but is permitted to return the
+ userid based only on data present in the request; it neednt (and
+ shouldn't) check any persistent store to ensure that the user record
+ related to the request userid exists."""
def effective_principals(request):
""" Return a sequence representing the effective principals
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index c7fe86452..2e0514b01 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -282,6 +282,18 @@ class RendererHelper(object):
def get_renderer(self):
return self.renderer
+ def render_view(self, request, response, view, context):
+ system = {
+ 'view':view,
+ 'renderer_name':self.name, # b/c
+ 'renderer_info':{'name':self.name, 'package':self.package},
+ 'context':context,
+ 'request':request
+ }
+ return self.render_to_response(response, system,
+ request=request)
+
+
def render(self, value, system_values, request=None):
renderer = self.renderer
if system_values is None:
diff --git a/pyramid/request.py b/pyramid/request.py
index 74418f1bb..475df744a 100644
--- a/pyramid/request.py
+++ b/pyramid/request.py
@@ -24,7 +24,7 @@ class Request(WebobRequest):
argument.
The documentation below (save for the ``add_response_callback`` and
- ''add_finished_callback`` methods, which are defined in this subclass
+ ``add_finished_callback`` methods, which are defined in this subclass
itself, and the attributes ``context``, ``registry``, ``root``,
``subpath``, ``traversed``, ``view_name``, ``virtual_root`` , and
``virtual_root_path``, each of which is added to the request by the
diff --git a/pyramid/security.py b/pyramid/security.py
index 723e87a87..51c0802d5 100644
--- a/pyramid/security.py
+++ b/pyramid/security.py
@@ -64,6 +64,24 @@ def authenticated_userid(request):
return None
return policy.authenticated_userid(request)
+def unauthenticated_userid(request):
+ """ Return an object which represents the *claimed* (not verified) user
+ id of the credentials present in the request. ``None`` if there is no
+ :term:`authentication policy` in effect or there is no user data
+ associated with the current request. This differs from
+ :func:`~pyramid.security.authenticated_userid`, because the effective
+ authentication policy will not ensure that a record associated with the
+ userid exists in persistent storage."""
+ try:
+ reg = request.registry
+ except AttributeError:
+ reg = get_current_registry() # b/c
+
+ policy = reg.queryUtility(IAuthenticationPolicy)
+ if policy is None:
+ return None
+ return policy.unauthenticated_userid(request)
+
def effective_principals(request):
""" Return the list of 'effective' :term:`principal` identifiers
for the ``request``. This will include the userid of the
diff --git a/pyramid/testing.py b/pyramid/testing.py
index 61bb1843a..15fc385cd 100644
--- a/pyramid/testing.py
+++ b/pyramid/testing.py
@@ -44,9 +44,10 @@ def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True):
: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
- named :func:`pyramid.security.has_permission`,
+ This function is most useful when testing code that uses the APIs named
+ :func:`pyramid.security.has_permission`,
:func:`pyramid.security.authenticated_userid`,
+ :func:`pyramid.security.unauthenticated_userid`,
:func:`pyramid.security.effective_principals`, and
:func:`pyramid.security.principals_allowed_by_permission`.
@@ -332,6 +333,9 @@ class DummySecurityPolicy(object):
def authenticated_userid(self, request):
return self.userid
+ def unauthenticated_userid(self, request):
+ return self.userid
+
def effective_principals(self, request):
effective_principals = [Everyone]
if self.userid:
diff --git a/pyramid/tests/test_authentication.py b/pyramid/tests/test_authentication.py
index d9d0c2c97..49d655466 100644
--- a/pyramid/tests/test_authentication.py
+++ b/pyramid/tests/test_authentication.py
@@ -18,11 +18,22 @@ class TestRepozeWho1AuthenticationPolicy(unittest.TestCase):
from pyramid.interfaces import IAuthenticationPolicy
verifyObject(IAuthenticationPolicy, self._makeOne())
+ def test_unauthenticated_userid_returns_None(self):
+ request = DummyRequest({})
+ policy = self._makeOne()
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_unauthenticated_userid(self):
+ request = DummyRequest(
+ {'repoze.who.identity':{'repoze.who.userid':'fred'}})
+ policy = self._makeOne()
+ self.assertEqual(policy.unauthenticated_userid(request), 'fred')
+
def test_authenticated_userid_None(self):
request = DummyRequest({})
policy = self._makeOne()
self.assertEqual(policy.authenticated_userid(request), None)
-
+
def test_authenticated_userid(self):
request = DummyRequest(
{'repoze.who.identity':{'repoze.who.userid':'fred'}})
@@ -132,6 +143,16 @@ class TestRemoteUserAuthenticationPolicy(unittest.TestCase):
from pyramid.interfaces import IAuthenticationPolicy
verifyObject(IAuthenticationPolicy, self._makeOne())
+ def test_unauthenticated_userid_returns_None(self):
+ request = DummyRequest({})
+ policy = self._makeOne()
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_unauthenticated_userid(self):
+ request = DummyRequest({'REMOTE_USER':'fred'})
+ policy = self._makeOne()
+ self.assertEqual(policy.unauthenticated_userid(request), 'fred')
+
def test_authenticated_userid_None(self):
request = DummyRequest({})
policy = self._makeOne()
@@ -196,6 +217,16 @@ class TestAutkTktAuthenticationPolicy(unittest.TestCase):
from pyramid.interfaces import IAuthenticationPolicy
verifyObject(IAuthenticationPolicy, self._makeOne(None, None))
+ def test_unauthenticated_userid_returns_None(self):
+ request = DummyRequest({})
+ policy = self._makeOne(None, None)
+ self.assertEqual(policy.unauthenticated_userid(request), None)
+
+ def test_unauthenticated_userid(self):
+ request = DummyRequest({'REMOTE_USER':'fred'})
+ policy = self._makeOne(None, {'userid':'fred'})
+ self.assertEqual(policy.unauthenticated_userid(request), 'fred')
+
def test_authenticated_userid_no_cookie_identity(self):
request = DummyRequest({})
policy = self._makeOne(None, None)
diff --git a/pyramid/tests/test_chameleon_text.py b/pyramid/tests/test_chameleon_text.py
index 79bc7984c..789c78bbe 100644
--- a/pyramid/tests/test_chameleon_text.py
+++ b/pyramid/tests/test_chameleon_text.py
@@ -1,23 +1,16 @@
import unittest
-from pyramid.testing import cleanUp
from pyramid.testing import skip_on
from pyramid import testing
class Base:
def setUp(self):
- cleanUp()
- import os
- try:
- # avoid spew from chameleon logger?
- os.unlink(self._getTemplatePath('minimal.txt.py'))
- except:
- pass
+ self.config = testing.setUp()
from zope.deprecation import __show__
__show__.off()
def tearDown(self):
- cleanUp()
+ testing.tearDown()
from zope.deprecation import __show__
__show__.on()
@@ -27,22 +20,10 @@ class Base:
return os.path.join(here, 'fixtures', name)
def _registerUtility(self, utility, iface, name=''):
- from pyramid.threadlocal import get_current_registry
- reg = get_current_registry()
+ reg = self.config.registry
reg.registerUtility(utility, iface, name=name)
- return reg
-
class TextTemplateRendererTests(Base, unittest.TestCase):
- def setUp(self):
- from pyramid.registry import Registry
- registry = Registry()
- self.config = testing.setUp(registry=registry)
- self.config.begin()
-
- def tearDown(self):
- self.config.end()
-
def _getTargetClass(self):
from pyramid.chameleon_text import TextTemplateRenderer
return TextTemplateRenderer
diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py
index 4601c2f12..4fceb809c 100644
--- a/pyramid/tests/test_chameleon_zpt.py
+++ b/pyramid/tests/test_chameleon_zpt.py
@@ -1,17 +1,16 @@
import unittest
-from pyramid.testing import cleanUp
from pyramid.testing import skip_on
from pyramid import testing
class Base(object):
def setUp(self):
- cleanUp()
+ self.config = testing.setUp()
from zope.deprecation import __show__
__show__.off()
def tearDown(self):
- cleanUp()
+ testing.tearDown()
from zope.deprecation import __show__
__show__.on()
@@ -21,21 +20,11 @@ class Base(object):
return os.path.join(here, 'fixtures', name)
def _registerUtility(self, utility, iface, name=''):
- from pyramid.threadlocal import get_current_registry
- reg = get_current_registry()
+ reg = self.config.registry
reg.registerUtility(utility, iface, name=name)
return reg
class ZPTTemplateRendererTests(Base, unittest.TestCase):
- def setUp(self):
- from pyramid.registry import Registry
- registry = Registry()
- self.config = testing.setUp(registry=registry)
- self.config.begin()
-
- def tearDown(self):
- self.config.end()
-
def _getTargetClass(self):
from pyramid.chameleon_zpt import ZPTTemplateRenderer
return ZPTTemplateRenderer
diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py
index c129b21ae..1632a4e5c 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/tests/test_config.py
@@ -63,27 +63,11 @@ class ConfiguratorTests(unittest.TestCase):
config.registry.registerHandler(subscriber, (event_iface,))
return L
- def _registerLogger(self, config):
- from pyramid.interfaces import IDebugLogger
- logger = DummyLogger()
- config.registry.registerUtility(logger, IDebugLogger)
- return logger
-
def _makeRequest(self, config):
request = DummyRequest()
request.registry = config.registry
return request
- def _registerSecurityPolicy(self, config, permissive):
- from pyramid.interfaces import IAuthenticationPolicy
- from pyramid.interfaces import IAuthorizationPolicy
- policy = DummySecurityPolicy(permissive)
- config.registry.registerUtility(policy, IAuthenticationPolicy)
- config.registry.registerUtility(policy, IAuthorizationPolicy)
-
- def _registerSettings(self, config, **settings):
- config.registry.settings = settings
-
def test_ctor_no_registry(self):
import sys
from pyramid.interfaces import ISettings
@@ -196,6 +180,13 @@ class ConfiguratorTests(unittest.TestCase):
config = self._makeOne(session_factory='factory')
self.assertEqual(config.registry.getUtility(ISessionFactory), 'factory')
+ def test_ctor_default_view_mapper(self):
+ from pyramid.interfaces import IViewMapperFactory
+ mapper = object()
+ config = self._makeOne(default_view_mapper=mapper)
+ self.assertEqual(config.registry.getUtility(IViewMapperFactory),
+ mapper)
+
def test_with_package_module(self):
from pyramid.tests import test_configuration
import pyramid.tests
@@ -739,6 +730,22 @@ class ConfiguratorTests(unittest.TestCase):
result = wrapper(None, None)
self.assertEqual(result, 'OK')
+ def test_add_view_with_decorator(self):
+ def view(request):
+ """ ABC """
+ return 'OK'
+ def view_wrapper(fn):
+ def inner(context, request):
+ return fn(context, request)
+ return inner
+ config = self._makeOne(autocommit=True)
+ config.add_view(view=view, decorator=view_wrapper)
+ wrapper = self._getViewCallable(config)
+ self.failIf(wrapper is view)
+ self.assertEqual(wrapper.__doc__, view.__doc__)
+ result = wrapper(None, None)
+ self.assertEqual(result, 'OK')
+
def test_add_view_as_instance(self):
class AView:
def __call__(self, context, request):
@@ -774,8 +781,10 @@ class ConfiguratorTests(unittest.TestCase):
config = self._makeOne(autocommit=True)
config.add_view(view=view)
wrapper = self._getViewCallable(config)
- result = wrapper(None, None)
+ request = self._makeRequest(config)
+ result = wrapper(None, request)
self.assertEqual(result, 'OK')
+ self.assertEqual(request.__view__.__class__, view)
def test_add_view_as_oldstyle_class_requestonly(self):
class view:
@@ -787,8 +796,11 @@ class ConfiguratorTests(unittest.TestCase):
config = self._makeOne(autocommit=True)
config.add_view(view=view)
wrapper = self._getViewCallable(config)
- result = wrapper(None, None)
+
+ request = self._makeRequest(config)
+ result = wrapper(None, request)
self.assertEqual(result, 'OK')
+ self.assertEqual(request.__view__.__class__, view)
def test_add_view_context_as_class(self):
from zope.interface import implementedBy
@@ -1410,6 +1422,27 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(result.name, fixture)
self.assertEqual(result.settings, settings)
+ def test_add_view_with_default_renderer(self):
+ class view(object):
+ def __init__(self, context, request):
+ self.request = request
+ self.context = context
+
+ def __call__(self):
+ return {'a':'1'}
+ config = self._makeOne(autocommit=True)
+ class moo(object):
+ def __init__(self, *arg, **kw):
+ pass
+ def __call__(self, *arg, **kw):
+ return 'moo'
+ config.add_renderer(None, moo)
+ config.add_view(view=view)
+ wrapper = self._getViewCallable(config)
+ request = self._makeRequest(config)
+ result = wrapper(None, request)
+ self.assertEqual(result.body, 'moo')
+
def test_add_view_with_template_renderer_no_callable(self):
import pyramid.tests
from pyramid.interfaces import ISettings
@@ -1964,6 +1997,33 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(view['attr'], 'action')
self.assertEqual(view['view'], MyView)
+ def test_add_handler_with_action_decorator(self):
+ config = self._makeOne(autocommit=True)
+ views = []
+ def dummy_add_view(**kw):
+ views.append(kw)
+ config.add_view = dummy_add_view
+ class MyHandler(object):
+ @classmethod
+ def __action_decorator__(cls, fn): # pragma: no cover
+ return fn
+ def action(self): # pragma: no cover
+ return 'response'
+ config.add_handler('name', '/{action}', MyHandler)
+ self.assertEqual(len(views), 1)
+ self.assertEqual(views[0]['decorator'], MyHandler.__action_decorator__)
+
+ def test_add_handler_with_action_decorator_fail_on_instancemethod(self):
+ config = self._makeOne(autocommit=True)
+ class MyHandler(object):
+ def __action_decorator__(self, fn): # pragma: no cover
+ return fn
+ def action(self): # pragma: no cover
+ return 'response'
+ from pyramid.exceptions import ConfigurationError
+ self.assertRaises(ConfigurationError, config.add_handler,
+ 'name', '/{action}', MyHandler)
+
def test_add_handler_doesnt_mutate_expose_dict(self):
config = self._makeOne(autocommit=True)
views = []
@@ -2274,7 +2334,8 @@ class ConfiguratorTests(unittest.TestCase):
request_type = self._getRouteRequestIface(config, 'name')
wrapper = self._getViewCallable(config, None, request_type)
self._assertRoute(config, 'name', 'path')
- self.assertEqual(wrapper(None, None), 'OK')
+ request = self._makeRequest(config)
+ self.assertEqual(wrapper(None, request), 'OK')
def test_add_route_with_view_renderer_alias(self):
config = self._makeOne(autocommit=True)
@@ -2371,6 +2432,52 @@ class ConfiguratorTests(unittest.TestCase):
else: # pragma: no cover
raise AssertionError
+ def test_derive_view_function(self):
+ def view(request):
+ return 'OK'
+ config = self._makeOne()
+ result = config.derive_view(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None), 'OK')
+
+ def test_derive_view_dottedname(self):
+ config = self._makeOne()
+ result = config.derive_view(
+ 'pyramid.tests.test_config.dummy_view')
+ self.failIf(result is dummy_view)
+ self.assertEqual(result(None, None), 'OK')
+
+ def test_derive_view_with_default_renderer_no_explicit_renderer(self):
+ config = self._makeOne()
+ class moo(object):
+ def __init__(self, view):
+ pass
+ def __call__(self, *arg, **kw):
+ return 'moo'
+ config.add_renderer(None, moo)
+ def view(request):
+ return 'OK'
+ 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):
+ class moo(object): pass
+ class foo(object):
+ def __init__(self, view):
+ pass
+ def __call__(self, *arg, **kw):
+ return 'foo'
+ def view(request):
+ return 'OK'
+ config = self._makeOne()
+ config.add_renderer(None, moo)
+ config.add_renderer('foo', foo)
+ result = config.derive_view(view, renderer='foo')
+ self.failIf(result is view)
+ request = self._makeRequest(config)
+ self.assertEqual(result(None, request).body, 'foo')
+
def test__override_not_yet_registered(self):
from pyramid.interfaces import IPackageOverrides
package = DummyPackage('package')
@@ -2567,6 +2674,22 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(config.registry.getUtility(IDefaultPermission),
'view')
+ def test_add_view_mapper(self):
+ from pyramid.interfaces import IViewMapperFactory
+ config = self._makeOne(autocommit=True)
+ mapper = object()
+ config.set_view_mapper(mapper)
+ result = config.registry.getUtility(IViewMapperFactory)
+ self.assertEqual(result, mapper)
+
+ def test_add_view_mapper_dottedname(self):
+ from pyramid.interfaces import IViewMapperFactory
+ config = self._makeOne(autocommit=True)
+ config.set_view_mapper('pyramid.tests.test_config')
+ result = config.registry.getUtility(IViewMapperFactory)
+ from pyramid.tests import test_config
+ self.assertEqual(result, test_config)
+
def test_set_session_factory(self):
from pyramid.interfaces import ISessionFactory
config = self._makeOne(autocommit=True)
@@ -2614,404 +2737,6 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(config.registry.getUtility(ITranslationDirectories),
[locale])
- def test_derive_view_function(self):
- def view(request):
- return 'OK'
- config = self._makeOne()
- result = config.derive_view(view)
- self.failIf(result is view)
- self.assertEqual(result(None, None), 'OK')
-
- def test_derive_view_dottedname(self):
- config = self._makeOne()
- result = config.derive_view(
- 'pyramid.tests.test_config.dummy_view')
- self.failIf(result is dummy_view)
- self.assertEqual(result(None, None), 'OK')
-
- def test_derive_view_with_renderer(self):
- def view(request):
- return 'OK'
- config = self._makeOne(autocommit=True)
- class moo(object):
- def __init__(self, *arg, **kw):
- pass
- def __call__(self, *arg, **kw):
- return 'moo'
- config.add_renderer('moo', moo)
- result = config.derive_view(view, renderer='moo')
- 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(autocommit=True)
- 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(autocommit=True)
- 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):
- pass
- def __call__(self):
- return 'OK'
- config = self._makeOne()
- result = config.derive_view(View)
- self.assertEqual(result(None, None), 'OK')
-
- def test_derive_view_class_with_attr(self):
- class View(object):
- def __init__(self, request):
- pass
- def another(self):
- return 'OK'
- config = self._makeOne()
- result = config.derive_view(View, attr='another')
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_function_context_and_request(self):
- def view(context, request):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failUnless(result is view)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(view(None, None), 'OK')
-
- def test__derive_view_as_function_requestonly(self):
- def view(request):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_newstyle_class_context_and_request(self):
- class view(object):
- def __init__(self, context, request):
- pass
- def __call__(self):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_newstyle_class_requestonly(self):
- class view(object):
- def __init__(self, context, request):
- pass
- def __call__(self):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_oldstyle_class_context_and_request(self):
- class view:
- def __init__(self, context, request):
- pass
- def __call__(self):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_oldstyle_class_requestonly(self):
- class view:
- def __init__(self, context, request):
- pass
- def __call__(self):
- return 'OK'
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_instance_context_and_request(self):
- class View:
- def __call__(self, context, request):
- return 'OK'
- view = View()
- config = self._makeOne()
- result = config._derive_view(view)
- self.failUnless(result is view)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_as_instance_requestonly(self):
- class View:
- def __call__(self, request):
- return 'OK'
- view = View()
- config = self._makeOne()
- result = config._derive_view(view)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.failUnless('instance' in result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- self.assertEqual(result(None, None), 'OK')
-
- def test__derive_view_with_debug_authorization_no_authpol(self):
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config,
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger(config)
- result = config._derive_view(view, permission='view')
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), 'OK')
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): Allowed "
- "(no authorization policy in use)")
-
- def test__derive_view_with_debug_authorization_no_permission(self):
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config,
- debug_authorization=True, reload_templates=True)
- self._registerSecurityPolicy(config, True)
- logger = self._registerLogger(config)
- result = config._derive_view(view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), 'OK')
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): Allowed ("
- "no permission registered)")
-
- def test__derive_view_debug_auth_permission_authpol_permitted(self):
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config, debug_authorization=True,
- reload_templates=True)
- logger = self._registerLogger(config)
- self._registerSecurityPolicy(config, True)
- result = config._derive_view(view, permission='view')
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result.__call_permissive__, view)
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), 'OK')
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): True")
-
- def test__derive_view_debug_auth_permission_authpol_denied(self):
- from pyramid.exceptions import Forbidden
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config,
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger(config)
- self._registerSecurityPolicy(config, False)
- result = config._derive_view(view, permission='view')
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result.__call_permissive__, view)
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertRaises(Forbidden, result, None, request)
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): False")
-
- def test__derive_view_debug_auth_permission_authpol_denied2(self):
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config,
- debug_authorization=True, reload_templates=True)
- self._registerLogger(config)
- self._registerSecurityPolicy(config, False)
- result = config._derive_view(view, permission='view')
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- permitted = result.__permitted__(None, None)
- self.assertEqual(permitted, False)
-
- def test__derive_view_debug_auth_permission_authpol_overridden(self):
- view = lambda *arg: 'OK'
- config = self._makeOne()
- self._registerSettings(config,
- debug_authorization=True, reload_templates=True)
- logger = self._registerLogger(config)
- self._registerSecurityPolicy(config, False)
- result = config._derive_view(view,
- permission='__no_permission_required__')
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.failIf(hasattr(result, '__call_permissive__'))
- request = self._makeRequest(config)
- request.view_name = 'view_name'
- request.url = 'url'
- self.assertEqual(result(None, request), 'OK')
- self.assertEqual(len(logger.messages), 1)
- self.assertEqual(logger.messages[0],
- "debug_authorization of url url (view name "
- "'view_name' against context None): False")
-
- def test__derive_view_with_predicates_all(self):
- view = lambda *arg: 'OK'
- predicates = []
- def predicate1(context, request):
- predicates.append(True)
- return True
- def predicate2(context, request):
- predicates.append(True)
- return True
- config = self._makeOne()
- result = config._derive_view(view, predicates=[predicate1, predicate2])
- request = self._makeRequest(config)
- request.method = 'POST'
- next = result(None, None)
- self.assertEqual(next, 'OK')
- self.assertEqual(predicates, [True, True])
-
- def test__derive_view_with_predicates_checker(self):
- view = lambda *arg: 'OK'
- predicates = []
- def predicate1(context, request):
- predicates.append(True)
- return True
- def predicate2(context, request):
- predicates.append(True)
- return True
- config = self._makeOne()
- result = config._derive_view(view, predicates=[predicate1, predicate2])
- request = self._makeRequest(config)
- request.method = 'POST'
- next = result.__predicated__(None, None)
- self.assertEqual(next, True)
- self.assertEqual(predicates, [True, True])
-
- def test__derive_view_with_predicates_notall(self):
- from pyramid.exceptions import NotFound
- view = lambda *arg: 'OK'
- predicates = []
- def predicate1(context, request):
- predicates.append(True)
- return True
- def predicate2(context, request):
- predicates.append(True)
- return False
- config = self._makeOne()
- result = config._derive_view(view, predicates=[predicate1, predicate2])
- request = self._makeRequest(config)
- request.method = 'POST'
- self.assertRaises(NotFound, result, None, None)
- self.assertEqual(predicates, [True, True])
-
- def test__derive_view_with_wrapper_viewname(self):
- from webob import Response
- from pyramid.interfaces import IView
- from pyramid.interfaces import IViewClassifier
- inner_response = Response('OK')
- def inner_view(context, request):
- return inner_response
- def outer_view(context, request):
- self.assertEqual(request.wrapped_response, inner_response)
- self.assertEqual(request.wrapped_body, inner_response.body)
- self.assertEqual(request.wrapped_view, inner_view)
- return Response('outer ' + request.wrapped_body)
- config = self._makeOne()
- config.registry.registerAdapter(
- outer_view, (IViewClassifier, None, None), IView, 'owrap')
- result = config._derive_view(inner_view, viewname='inner',
- wrapper_viewname='owrap')
- self.failIf(result is inner_view)
- self.assertEqual(inner_view.__module__, result.__module__)
- self.assertEqual(inner_view.__doc__, result.__doc__)
- request = self._makeRequest(config)
- request.registry = config.registry
- response = result(None, request)
- self.assertEqual(response.body, 'outer OK')
-
- def test__derive_view_with_wrapper_viewname_notfound(self):
- from webob import Response
- inner_response = Response('OK')
- def inner_view(context, request):
- return inner_response
- config = self._makeOne()
- request = self._makeRequest(config)
- request.registry = config.registry
- wrapped = config._derive_view(
- inner_view, viewname='inner', wrapper_viewname='owrap')
- self.assertRaises(ValueError, wrapped, None, request)
-
def test_override_asset_samename(self):
from pyramid.exceptions import ConfigurationError
config = self._makeOne()
@@ -3491,353 +3216,840 @@ class ConfiguratorTests(unittest.TestCase):
for confinst in conflict:
yield confinst[2]
-class Test__map_view(unittest.TestCase):
+class TestViewDeriver(unittest.TestCase):
def setUp(self):
- from pyramid.registry import Registry
- self.registry = Registry()
- testing.setUp(registry=self.registry)
+ self.config = testing.setUp()
def tearDown(self):
- del self.registry
- testing.tearDown()
-
- def _registerRenderer(self, typ='.txt'):
- from pyramid.interfaces import IRendererFactory
- from pyramid.interfaces import ITemplateRenderer
- from zope.interface import implements
- class Renderer:
- implements(ITemplateRenderer)
- spec = 'abc' + typ
- def __init__(self, path):
- self.__class__.path = path
- def __call__(self, *arg):
- return 'Hello!'
- self.registry.registerUtility(Renderer, IRendererFactory, name=typ)
- return Renderer
-
+ self.config = None
+
+ def _makeOne(self, **kw):
+ kw['registry'] = self.config.registry
+ from pyramid.config import ViewDeriver
+ return ViewDeriver(**kw)
+
def _makeRequest(self):
request = DummyRequest()
- request.registry = self.registry
+ request.registry = self.config.registry
return request
- def _callFUT(self, view, **kw):
- from pyramid.config import _map_view
- return _map_view(view, self.registry, **kw)
+ def _registerLogger(self):
+ from pyramid.interfaces import IDebugLogger
+ logger = DummyLogger()
+ self.config.registry.registerUtility(logger, IDebugLogger)
+ return logger
- def test__map_view_as_function_context_and_request(self):
- def view(context, request):
+ def _registerSecurityPolicy(self, permissive):
+ from pyramid.interfaces import IAuthenticationPolicy
+ from pyramid.interfaces import IAuthorizationPolicy
+ policy = DummySecurityPolicy(permissive)
+ self.config.registry.registerUtility(policy, IAuthenticationPolicy)
+ self.config.registry.registerUtility(policy, IAuthorizationPolicy)
+
+ def test_requestonly_function(self):
+ def view(request):
return 'OK'
- result = self._callFUT(view)
- self.failUnless(result is view)
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.failIf(result is view)
self.assertEqual(result(None, None), 'OK')
- def test__map_view_as_function_with_attr(self):
- def view(context, request):
- """ """
- result = self._callFUT(view, attr='__name__')
+ def test_requestonly_function_with_renderer(self):
+ class moo(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, 'OK')
+ self.assertEqual(view_inst, view)
+ self.assertEqual(ctx, context)
+ return 'moo'
+ def view(request):
+ return 'OK'
+ deriver = self._makeOne(renderer=moo())
+ result = deriver(view)
self.failIf(result is view)
- self.assertRaises(TypeError, result, None, None)
-
- def test__map_view_as_function_with_attr_and_renderer(self):
- renderer = self._registerRenderer()
- view = lambda *arg: 'OK'
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='__name__', renderer=info)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), 'moo')
+
+ def test_requestonly_function_with_renderer_request_override(self):
+ def moo(info):
+ def inner(value, system):
+ self.assertEqual(value, 'OK')
+ self.assertEqual(system['request'], request)
+ self.assertEqual(system['context'], context)
+ return 'moo'
+ return inner
+ def view(request):
+ return 'OK'
+ self.config.add_renderer('moo', moo)
+ deriver = self._makeOne(renderer='string')
+ result = deriver(view)
self.failIf(result is view)
- self.assertRaises(TypeError, result, None, None)
+ request = self._makeRequest()
+ request.override_renderer = 'moo'
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request).body, 'moo')
- def test__map_view_as_function_requestonly(self):
+ def test_requestonly_function_with_renderer_request_has_view(self):
+ class moo(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, 'OK')
+ self.assertEqual(view_inst, 'view')
+ self.assertEqual(ctx, context)
+ return 'moo'
def view(request):
return 'OK'
- result = self._callFUT(view)
+ deriver = self._makeOne(renderer=moo())
+ result = deriver(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ request.__view__ = 'view'
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), 'moo')
+ self.failIf(hasattr(request, '__view__'))
+
+ def test_class_without_attr(self):
+ class View(object):
+ def __init__(self, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ deriver = self._makeOne()
+ result = deriver(View)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, View)
+
+ def test_class_with_attr(self):
+ class View(object):
+ def __init__(self, request):
+ pass
+ def another(self):
+ return 'OK'
+ deriver = self._makeOne(attr='another')
+ result = deriver(View)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, View)
+
+ def test_as_function_context_and_request(self):
+ def view(context, request):
+ return 'OK'
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.failUnless(result is view)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ self.assertEqual(view(None, None), 'OK')
- def test__map_view_as_function_requestonly_with_attr(self):
+ def test_as_function_requestonly(self):
def view(request):
- """ """
- result = self._callFUT(view, attr='__name__')
+ return 'OK'
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
self.assertEqual(view.__name__, result.__name__)
- self.assertRaises(TypeError, result, None, None)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ self.assertEqual(result(None, None), 'OK')
- def test__map_view_as_newstyle_class_context_and_request(self):
+ def test_as_newstyle_class_context_and_request(self):
class view(object):
def __init__(self, context, request):
pass
def __call__(self):
return 'OK'
- result = self._callFUT(view)
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, view)
- def test__map_view_as_newstyle_class_context_and_request_with_attr(self):
+ def test_as_newstyle_class_requestonly(self):
class view(object):
def __init__(self, context, request):
pass
- def index(self):
+ def __call__(self):
return 'OK'
- result = self._callFUT(view, attr='index')
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, view)
- def test__map_view_as_newstyle_class_context_and_request_attr_and_renderer(
- self):
- renderer = self._registerRenderer()
- class view(object):
+ def test_as_oldstyle_class_context_and_request(self):
+ class view:
def __init__(self, context, request):
pass
- def index(self):
- return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
+ def __call__(self):
+ return 'OK'
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
self.assertEqual(view.__name__, result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, view)
- def test__map_view_as_newstyle_class_requestonly(self):
- class view(object):
- def __init__(self, request):
+ def test_as_oldstyle_class_requestonly(self):
+ class view:
+ def __init__(self, context, request):
pass
def __call__(self):
return 'OK'
- result = self._callFUT(view)
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
self.assertEqual(view.__name__, result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(request.__view__.__class__, view)
+
+ def test_as_instance_context_and_request(self):
+ class View:
+ def __call__(self, context, request):
+ return 'OK'
+ view = View()
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.failUnless(result is view)
+ self.failIf(hasattr(result, '__call_permissive__'))
self.assertEqual(result(None, None), 'OK')
- def test__map_view_as_newstyle_class_requestonly_with_attr(self):
- class view(object):
- def __init__(self, request):
- pass
- def index(self):
+ def test_as_instance_requestonly(self):
+ class View:
+ def __call__(self, request):
return 'OK'
- result = self._callFUT(view, attr='index')
+ view = View()
+ deriver = self._makeOne()
+ result = deriver(view)
self.failIf(result is view)
self.assertEqual(view.__module__, result.__module__)
self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
+ self.failUnless('instance' in result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
self.assertEqual(result(None, None), 'OK')
- def test__map_view_as_newstyle_class_requestonly_attr_and_renderer(self):
- renderer = self._registerRenderer()
- class view(object):
+ def test_with_debug_authorization_no_authpol(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): Allowed "
+ "(no authorization policy in use)")
+
+ def test_with_debug_authorization_no_permission(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ self._registerSecurityPolicy(True)
+ logger = self._registerLogger()
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): Allowed ("
+ "no permission registered)")
+
+ def test_debug_auth_permission_authpol_permitted(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ self._registerSecurityPolicy(True)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertEqual(result.__call_permissive__, view)
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): True")
+
+ def test_debug_auth_permission_authpol_denied(self):
+ from pyramid.exceptions import Forbidden
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ self._registerSecurityPolicy(False)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.assertEqual(result.__call_permissive__, view)
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertRaises(Forbidden, result, None, request)
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): False")
+
+ def test_debug_auth_permission_authpol_denied2(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ self._registerLogger()
+ self._registerSecurityPolicy(False)
+ deriver = self._makeOne(permission='view')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ permitted = result.__permitted__(None, None)
+ self.assertEqual(permitted, False)
+
+ def test_debug_auth_permission_authpol_overridden(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(
+ debug_authorization=True, reload_templates=True)
+ logger = self._registerLogger()
+ self._registerSecurityPolicy(False)
+ deriver = self._makeOne(permission='__no_permission_required__')
+ result = deriver(view)
+ self.assertEqual(view.__module__, result.__module__)
+ self.assertEqual(view.__doc__, result.__doc__)
+ self.assertEqual(view.__name__, result.__name__)
+ self.failIf(hasattr(result, '__call_permissive__'))
+ request = self._makeRequest()
+ request.view_name = 'view_name'
+ request.url = 'url'
+ self.assertEqual(result(None, request), 'OK')
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url url (view name "
+ "'view_name' against context None): False")
+
+ def test_with_predicates_all(self):
+ view = lambda *arg: 'OK'
+ predicates = []
+ def predicate1(context, request):
+ predicates.append(True)
+ return True
+ def predicate2(context, request):
+ predicates.append(True)
+ return True
+ deriver = self._makeOne(predicates=[predicate1, predicate2])
+ result = deriver(view)
+ request = self._makeRequest()
+ request.method = 'POST'
+ next = result(None, None)
+ self.assertEqual(next, 'OK')
+ self.assertEqual(predicates, [True, True])
+
+ def test_with_predicates_checker(self):
+ view = lambda *arg: 'OK'
+ predicates = []
+ def predicate1(context, request):
+ predicates.append(True)
+ return True
+ def predicate2(context, request):
+ predicates.append(True)
+ return True
+ deriver = self._makeOne(predicates=[predicate1, predicate2])
+ result = deriver(view)
+ request = self._makeRequest()
+ request.method = 'POST'
+ next = result.__predicated__(None, None)
+ self.assertEqual(next, True)
+ self.assertEqual(predicates, [True, True])
+
+ def test_with_predicates_notall(self):
+ from pyramid.exceptions import NotFound
+ view = lambda *arg: 'OK'
+ predicates = []
+ def predicate1(context, request):
+ predicates.append(True)
+ return True
+ def predicate2(context, request):
+ predicates.append(True)
+ return False
+ deriver = self._makeOne(predicates=[predicate1, predicate2])
+ result = deriver(view)
+ request = self._makeRequest()
+ request.method = 'POST'
+ self.assertRaises(NotFound, result, None, None)
+ self.assertEqual(predicates, [True, True])
+
+ def test_with_wrapper_viewname(self):
+ from webob import Response
+ from pyramid.interfaces import IView
+ from pyramid.interfaces import IViewClassifier
+ inner_response = Response('OK')
+ def inner_view(context, request):
+ return inner_response
+ def outer_view(context, request):
+ self.assertEqual(request.wrapped_response, inner_response)
+ self.assertEqual(request.wrapped_body, inner_response.body)
+ self.assertEqual(request.wrapped_view, inner_view)
+ return Response('outer ' + request.wrapped_body)
+ self.config.registry.registerAdapter(
+ outer_view, (IViewClassifier, None, None), IView, 'owrap')
+ deriver = self._makeOne(viewname='inner',
+ wrapper_viewname='owrap')
+ result = deriver(inner_view)
+ self.failIf(result is inner_view)
+ self.assertEqual(inner_view.__module__, result.__module__)
+ self.assertEqual(inner_view.__doc__, result.__doc__)
+ request = self._makeRequest()
+ response = result(None, request)
+ self.assertEqual(response.body, 'outer OK')
+
+ def test_with_wrapper_viewname_notfound(self):
+ from webob import Response
+ inner_response = Response('OK')
+ def inner_view(context, request):
+ return inner_response
+ deriver = self._makeOne(viewname='inner', wrapper_viewname='owrap')
+ wrapped = deriver(inner_view)
+ request = self._makeRequest()
+ self.assertRaises(ValueError, wrapped, None, request)
+
+ def test_as_newstyle_class_context_and_request_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return resp
+ class View(object):
+ def __init__(self, context, request):
+ pass
+ def index(self):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.failIf(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_as_newstyle_class_requestonly_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return resp
+ class View(object):
+ def __init__(self, request):
+ pass
+ def index(self):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.failIf(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_as_oldstyle_cls_context_request_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return resp
+ class View:
+ def __init__(self, context, request):
+ pass
+ def index(self):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.failIf(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_as_oldstyle_cls_requestonly_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst.__class__, View)
+ self.assertEqual(ctx, context)
+ return resp
+ class View:
def __init__(self, request):
pass
def index(self):
return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ result = deriver(View)
+ self.failIf(result is View)
+ self.assertEqual(result.__module__, View.__module__)
+ self.assertEqual(result.__doc__, View.__doc__)
+ self.assertEqual(result.__name__, View.__name__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_as_instance_context_and_request_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst, view)
+ self.assertEqual(ctx, context)
+ return resp
+ class View:
+ def index(self, context, request):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ view = View()
+ result = deriver(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
+ self.assertEqual(result.__module__, view.__module__)
+ self.assertEqual(result.__doc__, view.__doc__)
+ request = self._makeRequest()
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_as_instance_requestonly_attr_and_renderer(self):
+ class renderer(object):
+ def render_view(inself, req, resp, view_inst, ctx):
+ self.assertEqual(req, request)
+ self.assertEqual(resp, {'a':'1'})
+ self.assertEqual(view_inst, view)
+ self.assertEqual(ctx, context)
+ return resp
+ class View:
+ def index(self, request):
+ return {'a':'1'}
+ deriver = self._makeOne(renderer=renderer(), attr='index')
+ view = View()
+ result = deriver(view)
+ self.failIf(result is view)
+ self.assertEqual(result.__module__, view.__module__)
+ self.assertEqual(result.__doc__, view.__doc__)
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ context = testing.DummyResource()
+ self.assertEqual(result(context, request), {'a':'1'})
+
+ def test_with_view_mapper_config_specified(self):
+ class mapper(object):
+ def __init__(self, **kw):
+ self.kw = kw
+ def __call__(self, view):
+ def wrapped(context, request):
+ return 'OK'
+ return wrapped
+ def view(context, request):
+ return 'NOTOK'
+ deriver = self._makeOne(view_mapper=mapper)
+ result = deriver(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None), 'OK')
- def test__map_view_as_oldstyle_class_context_and_request(self):
- class view:
+ def test_with_view_mapper_view_specified(self):
+ def mapper(**kw):
+ def inner(view):
+ def superinner(context, request):
+ self.assertEqual(request, None)
+ return 'OK'
+ return superinner
+ return inner
+ def view(context, request):
+ return 'NOTOK'
+ view.__view_mapper__ = mapper
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None), 'OK')
+
+ def test_with_view_mapper_default_mapper_specified(self):
+ def mapper(**kw):
+ def inner(view):
+ def superinner(context, request):
+ self.assertEqual(request, None)
+ return 'OK'
+ return superinner
+ return inner
+ self.config.set_view_mapper(mapper)
+ def view(context, request):
+ return 'NOTOK'
+ deriver = self._makeOne()
+ result = deriver(view)
+ self.failIf(result is view)
+ self.assertEqual(result(None, None), 'OK')
+
+class TestDefaultViewMapper(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+ self.registry = self.config.registry
+
+ def tearDown(self):
+ del self.registry
+ testing.tearDown()
+
+ def _makeOne(self, **kw):
+ from pyramid.config import DefaultViewMapper
+ kw['registry'] = self.registry
+ return DefaultViewMapper(**kw)
+
+ def _makeRequest(self):
+ request = DummyRequest()
+ request.registry = self.registry
+ return request
+
+ def test_view_as_function_context_and_request(self):
+ def view(context, request):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.failUnless(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test__view_as_function_with_attr(self):
+ def view(context, request):
+ """ """
+ mapper = self._makeOne(attr='__name__')
+ result = mapper(view)
+ self.failIf(result is view)
+ request = self._makeRequest()
+ self.assertRaises(TypeError, result, None, request)
+
+ def test_view_as_function_requestonly(self):
+ def view(request):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.failIf(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_function_requestonly_with_attr(self):
+ def view(request):
+ """ """
+ mapper = self._makeOne(attr='__name__')
+ result = mapper(view)
+ self.failIf(result is view)
+ request = self._makeRequest()
+ self.assertRaises(TypeError, result, None, request)
+
+ def test_view_as_newstyle_class_context_and_request(self):
+ class view(object):
def __init__(self, context, request):
pass
def __call__(self):
return 'OK'
- result = self._callFUT(view)
+ mapper = self._makeOne()
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_oldstyle_class_context_and_request_with_attr(self):
- class view:
+ def test_view_as_newstyle_class_context_and_request_with_attr(self):
+ class view(object):
def __init__(self, context, request):
pass
def index(self):
return 'OK'
- result = self._callFUT(view, attr='index')
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_oldstyle_cls_context_request_attr_and_renderer(self):
- renderer = self._registerRenderer()
- class view:
- def __init__(self, context, request):
+ def test_view_as_newstyle_class_requestonly(self):
+ class view(object):
+ def __init__(self, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.failIf(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_newstyle_class_requestonly_with_attr(self):
+ class view(object):
+ def __init__(self, request):
pass
def index(self):
- return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
+ return 'OK'
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_oldstyle_class_requestonly(self):
+ def test_view_as_oldstyle_class_context_and_request(self):
class view:
- def __init__(self, request):
+ def __init__(self, context, request):
pass
def __call__(self):
return 'OK'
- result = self._callFUT(view)
+ mapper = self._makeOne()
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_oldstyle_class_requestonly_with_attr(self):
+ def test_view_as_oldstyle_class_context_and_request_with_attr(self):
class view:
- def __init__(self, request):
+ def __init__(self, context, request):
pass
def index(self):
return 'OK'
- result = self._callFUT(view, attr='index')
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
+
+ def test_view_as_oldstyle_class_requestonly(self):
+ class view:
+ def __init__(self, request):
+ pass
+ def __call__(self):
+ return 'OK'
+ mapper = self._makeOne()
+ result = mapper(view)
+ self.failIf(result is view)
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_oldstyle_class_requestonly_attr_and_renderer(self):
- renderer = self._registerRenderer()
+ def test_view_as_oldstyle_class_requestonly_with_attr(self):
class view:
def __init__(self, request):
pass
def index(self):
- return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
+ return 'OK'
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.assertEqual(view.__name__, result.__name__)
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_instance_context_and_request(self):
+ def test_view_as_instance_context_and_request(self):
class View:
def __call__(self, context, request):
return 'OK'
view = View()
- result = self._callFUT(view)
+ mapper = self._makeOne()
+ result = mapper(view)
self.failUnless(result is view)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_instance_context_and_request_and_attr(self):
+ def test_view_as_instance_context_and_request_and_attr(self):
class View:
def index(self, context, request):
return 'OK'
view = View()
- result = self._callFUT(view, attr='index')
- self.failIf(result is view)
- self.assertEqual(result(None, None), 'OK')
-
- def test__map_view_as_instance_context_and_request_attr_and_renderer(self):
- renderer = self._registerRenderer()
- class View:
- def index(self, context, request):
- return {'a':'1'}
- view = View()
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_instance_requestonly(self):
+ def test_view_as_instance_requestonly(self):
class View:
def __call__(self, request):
return 'OK'
view = View()
- result = self._callFUT(view)
+ mapper = self._makeOne()
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.failUnless('instance' in result.__name__)
- self.assertEqual(result(None, None), 'OK')
+ request = self._makeRequest()
+ self.assertEqual(result(None, request), 'OK')
- def test__map_view_as_instance_requestonly_with_attr(self):
+ def test_view_as_instance_requestonly_with_attr(self):
class View:
def index(self, request):
return 'OK'
view = View()
- result = self._callFUT(view, attr='index')
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.failUnless('instance' in result.__name__)
- self.assertEqual(result(None, None), 'OK')
-
- def test__map_view_as_instance_requestonly_with_attr_and_renderer(self):
- renderer = self._registerRenderer()
- class View:
- def index(self, request):
- return {'a':'1'}
- view = View()
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, attr='index', renderer=info)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- self.failUnless('instance' in result.__name__)
- request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
-
- def test__map_view_rendereronly(self):
- renderer = self._registerRenderer()
- def view(context, request):
- return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, renderer=info)
- self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
- request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
-
- def test__map_view_with_registry(self):
- renderer = self._registerRenderer()
- def view(context, request):
- return {'a':'1'}
- info = {'name':renderer.spec, 'package':None}
- result = self._callFUT(view, renderer=info)
+ mapper = self._makeOne(attr='index')
+ result = mapper(view)
self.failIf(result is view)
- self.assertEqual(view.__module__, result.__module__)
- self.assertEqual(view.__doc__, result.__doc__)
request = self._makeRequest()
- self.assertEqual(result(None, request).body, 'Hello!')
+ self.assertEqual(result(None, request), 'OK')
-class Test_decorate_view(unittest.TestCase):
- def _callFUT(self, wrapped, original):
- from pyramid.config import decorate_view
- return decorate_view(wrapped, original)
+class Test_preserve_view_attrs(unittest.TestCase):
+ def _callFUT(self, view, wrapped_view):
+ from pyramid.config import preserve_view_attrs
+ return preserve_view_attrs(view, wrapped_view)
def test_it_same(self):
def view(context, request):
""" """
result = self._callFUT(view, view)
- self.assertEqual(result, False)
+ self.failUnless(result is view)
+
+ def test_it_different_with_existing_original_view(self):
+ def view1(context, request): pass
+ view1.__original_view__ = 'abc'
+ def view2(context, request): pass
+ result = self._callFUT(view1, view2)
+ self.assertEqual(result.__original_view__, 'abc')
+ self.failIf(result is view1)
def test_it_different(self):
class DummyView1:
@@ -3846,9 +4058,9 @@ class Test_decorate_view(unittest.TestCase):
__module__ = '1'
def __call__(self, context, request):
""" """
- def __call_permissive__(self, context, reuqest):
+ def __call_permissive__(self, context, request):
""" """
- def __predicated__(self, context, reuqest):
+ def __predicated__(self, context, request):
""" """
def __permitted__(self, context, request):
""" """
@@ -3858,16 +4070,17 @@ class Test_decorate_view(unittest.TestCase):
__module__ = '2'
def __call__(self, context, request):
""" """
- def __call_permissive__(self, context, reuqest):
+ def __call_permissive__(self, context, request):
""" """
- def __predicated__(self, context, reuqest):
+ def __predicated__(self, context, request):
""" """
def __permitted__(self, context, request):
""" """
view1 = DummyView1()
view2 = DummyView2()
- result = self._callFUT(view1, view2)
- self.assertEqual(result, True)
+ result = self._callFUT(view2, view1)
+ self.assertEqual(result, view1)
+ self.failUnless(view1.__original_view__ is view2)
self.failUnless(view1.__doc__ is view2.__doc__)
self.failUnless(view1.__module__ is view2.__module__)
self.failUnless(view1.__name__ is view2.__name__)
@@ -4333,24 +4546,23 @@ class TestMultiView(unittest.TestCase):
response = mv(context, request)
self.assertEqual(response, expected_response)
-
-class TestRequestOnly(unittest.TestCase):
- def _callFUT(self, arg):
+class Test_requestonly(unittest.TestCase):
+ def _callFUT(self, view, attr=None):
from pyramid.config import requestonly
- return requestonly(arg)
+ return requestonly(view, attr)
- def test_newstyle_class_no_init(self):
+ def test_requestonly_newstyle_class_no_init(self):
class foo(object):
""" """
self.assertFalse(self._callFUT(foo))
- def test_newstyle_class_init_toomanyargs(self):
+ def test_requestonly_newstyle_class_init_toomanyargs(self):
class foo(object):
def __init__(self, context, request):
""" """
self.assertFalse(self._callFUT(foo))
- def test_newstyle_class_init_onearg_named_request(self):
+ def test_requestonly_newstyle_class_init_onearg_named_request(self):
class foo(object):
def __init__(self, request):
""" """
@@ -4426,6 +4638,22 @@ class TestRequestOnly(unittest.TestCase):
""" """
self.assertFalse(self._callFUT(foo))
+ def test_function_with_attr_false(self):
+ def bar(context, request):
+ """ """
+ def foo(context, request):
+ """ """
+ foo.bar = bar
+ self.assertFalse(self._callFUT(foo, 'bar'))
+
+ def test_function_with_attr_true(self):
+ def bar(context, request):
+ """ """
+ def foo(request):
+ """ """
+ foo.bar = bar
+ self.assertTrue(self._callFUT(foo, 'bar'))
+
def test_function_onearg_named_request(self):
def foo(request):
""" """
diff --git a/pyramid/tests/test_security.py b/pyramid/tests/test_security.py
index dd9d48f45..94cefa642 100644
--- a/pyramid/tests/test_security.py
+++ b/pyramid/tests/test_security.py
@@ -224,6 +224,36 @@ class TestAuthenticatedUserId(unittest.TestCase):
result = self._callFUT(request)
self.assertEqual(result, 'yo')
+class TestUnauthenticatedUserId(unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
+ def _callFUT(self, request):
+ from pyramid.security import unauthenticated_userid
+ return unauthenticated_userid(request)
+
+ def test_no_authentication_policy(self):
+ request = _makeRequest()
+ result = self._callFUT(request)
+ self.assertEqual(result, None)
+
+ def test_with_authentication_policy(self):
+ request = _makeRequest()
+ _registerAuthenticationPolicy(request.registry, 'yo')
+ result = self._callFUT(request)
+ self.assertEqual(result, 'yo')
+
+ def test_with_authentication_policy_no_reg_on_request(self):
+ from pyramid.threadlocal import get_current_registry
+ request = DummyRequest({})
+ registry = get_current_registry()
+ _registerAuthenticationPolicy(registry, 'yo')
+ result = self._callFUT(request)
+ self.assertEqual(result, 'yo')
+
class TestEffectivePrincipals(unittest.TestCase):
def setUp(self):
cleanUp()
@@ -355,6 +385,9 @@ class DummyAuthenticationPolicy:
def effective_principals(self, request):
return self.result
+ def unauthenticated_userid(self, request):
+ return self.result
+
def authenticated_userid(self, request):
return self.result
diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py
index ec6fdac5f..d2ed957f2 100644
--- a/pyramid/tests/test_testing.py
+++ b/pyramid/tests/test_testing.py
@@ -297,7 +297,11 @@ class TestDummySecurityPolicy(unittest.TestCase):
def test_authenticated_userid(self):
policy = self._makeOne('user')
self.assertEqual(policy.authenticated_userid(None), 'user')
-
+
+ def test_unauthenticated_userid(self):
+ policy = self._makeOne('user')
+ self.assertEqual(policy.unauthenticated_userid(None), 'user')
+
def test_effective_principals_userid(self):
policy = self._makeOne('user', ('group1',))
from pyramid.security import Everyone
diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py
index 79e363756..33c2b606d 100644
--- a/pyramid/tests/test_view.py
+++ b/pyramid/tests/test_view.py
@@ -229,11 +229,14 @@ class TestViewConfigDecorator(unittest.TestCase):
def test_create_nondefaults(self):
decorator = self._makeOne(name=None, request_type=None, for_=None,
- permission='foo')
+ permission='foo', view_mapper='mapper',
+ decorator='decorator')
self.assertEqual(decorator.name, None)
self.assertEqual(decorator.request_type, None)
self.assertEqual(decorator.context, None)
self.assertEqual(decorator.permission, 'foo')
+ self.assertEqual(decorator.view_mapper, 'mapper')
+ self.assertEqual(decorator.decorator, 'decorator')
def test_call_function(self):
decorator = self._makeOne()
@@ -317,10 +320,9 @@ class TestViewConfigDecorator(unittest.TestCase):
settings = call_venusian(venusian)
self.assertEqual(len(settings), 1)
renderer = settings[0]['renderer']
- self.assertEqual(renderer,
- {'name':'fixtures/minimal.pt',
- 'package':pyramid.tests,
- })
+ self.assertEqual(renderer.name, 'fixtures/minimal.pt')
+ self.assertEqual(renderer.package, pyramid.tests)
+ self.assertEqual(renderer.registry.__class__, DummyRegistry)
def test_call_with_renderer_dict(self):
decorator = self._makeOne(renderer={'a':1})
@@ -494,9 +496,13 @@ class DummyVenusian(object):
self.attachments.append((wrapped, callback, category))
return self.info
+class DummyRegistry(object):
+ pass
+
class DummyConfig(object):
def __init__(self):
self.settings = []
+ self.registry = DummyRegistry()
def add_view(self, **kw):
self.settings.append(kw)
diff --git a/pyramid/url.py b/pyramid/url.py
index e1eaaaa1e..c11e39143 100644
--- a/pyramid/url.py
+++ b/pyramid/url.py
@@ -32,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>
@@ -53,7 +53,7 @@ def route_url(route_name, request, *elements, **kw):
``*remainder`` replacement value, it is tacked on to the URL
untouched.
- If a keyword argument ``_query`` is present, it will used to
+ If a keyword argument ``_query`` is present, it will be used to
compose a query string that will be tacked on to the end of the
URL. The value of ``_query`` must be a sequence of two-tuples
*or* a data structure with an ``.items()`` method that returns a
@@ -221,7 +221,7 @@ def resource_url(resource, request, *elements, **kw):
``elements`` are used, the generated URL will *not*
end in trailing a slash.
- If a keyword argument ``query`` is present, it will used to
+ If a keyword argument ``query`` is present, it will be used to
compose a query string that will be tacked on to the end of the
URL. The value of ``query`` must be a sequence of two-tuples *or*
a data structure with an ``.items()`` method that returns a
diff --git a/pyramid/view.py b/pyramid/view.py
index 3dc110863..afd1c6d49 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -19,6 +19,7 @@ from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
from pyramid.httpexceptions import HTTPFound
+from pyramid.renderers import RendererHelper
from pyramid.static import static_view
from pyramid.threadlocal import get_current_registry
@@ -382,7 +383,8 @@ class view_config(object):
route_name=None, request_method=None, request_param=None,
containment=None, attr=None, renderer=None, wrapper=None,
xhr=False, accept=None, header=None, path_info=None,
- custom_predicates=(), context=None):
+ custom_predicates=(), context=None, decorator=None,
+ view_mapper=None):
self.name = name
self.request_type = request_type
self.context = context or for_
@@ -399,11 +401,19 @@ class view_config(object):
self.header = header
self.path_info = path_info
self.custom_predicates = custom_predicates
+ self.decorator = decorator
+ self.view_mapper = view_mapper
def __call__(self, wrapped):
settings = self.__dict__.copy()
def callback(context, name, ob):
+ renderer = settings.get('renderer')
+ if isinstance(renderer, basestring):
+ renderer = RendererHelper(name=renderer,
+ package=info.module,
+ registry=context.config.registry)
+ settings['renderer'] = renderer
context.config.add_view(view=ob, **settings)
info = self.venusian.attach(wrapped, callback, category='pyramid')
@@ -415,10 +425,6 @@ class view_config(object):
if settings['attr'] is None:
settings['attr'] = wrapped.__name__
- renderer_name = settings.get('renderer')
- if renderer_name is not None and not isinstance(renderer_name, dict):
- settings['renderer'] = {'name':renderer_name,
- 'package':info.module}
settings['_info'] = info.codeinfo
return wrapped
diff --git a/pyramid/zcml.py b/pyramid/zcml.py
index f668e3b4b..a2088e505 100644
--- a/pyramid/zcml.py
+++ b/pyramid/zcml.py
@@ -161,12 +161,7 @@ def view(
cacheable=True, # not used, here for b/w compat < 0.8
):
- if renderer is not None:
- package = getattr(_context, 'package', None)
- renderer = {'name':renderer, 'package':package}
-
context = context or for_
-
config = Configurator.with_context(_context)
config.add_view(
permission=permission, context=context, view=view, name=name,