summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CHANGES.txt12
-rw-r--r--LICENSE.txt10
-rw-r--r--TODO.txt6
-rw-r--r--docs/api/authentication.rst1
-rw-r--r--docs/api/config.rst2
-rw-r--r--docs/api/interfaces.rst15
-rw-r--r--docs/narr/hooks.rst6
-rw-r--r--docs/tutorials/wiki/authorization.rst218
-rw-r--r--pyramid/authentication.py29
-rw-r--r--pyramid/authorization.py23
-rw-r--r--pyramid/config.py62
-rw-r--r--pyramid/encode.py12
-rw-r--r--pyramid/interfaces.py25
-rw-r--r--pyramid/paster.py31
-rw-r--r--pyramid/renderers.py41
-rw-r--r--pyramid/static.py11
-rw-r--r--pyramid/tests/test_asset.py74
-rw-r--r--pyramid/tests/test_authorization.py23
-rw-r--r--pyramid/tests/test_config.py216
-rw-r--r--pyramid/tests/test_encode.py5
-rw-r--r--pyramid/tests/test_i18n.py43
-rw-r--r--pyramid/tests/test_paster.py75
-rw-r--r--pyramid/tests/test_renderers.py275
-rw-r--r--pyramid/tests/test_router.py8
-rw-r--r--pyramid/tests/test_static.py48
-rw-r--r--pyramid/tests/test_view.py21
-rw-r--r--pyramid/view.py26
28 files changed, 1030 insertions, 289 deletions
diff --git a/.gitignore b/.gitignore
index e36d9f7cf..ea97734f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,4 +22,5 @@ dist/
bin/
lib/
include/
+.idea/
distribute-*.tar.gz
diff --git a/CHANGES.txt b/CHANGES.txt
index 768a08b0a..0bd19572a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -13,6 +13,18 @@ Documentation
- The ``wiki2`` (SQLA+URL Dispatch) tutorial was updated slightly.
+- Make ``pyramid.interfaces.IAuthenticationPolicy`` and
+ ``pyramid.interfaces.IAuthorizationPolicy`` public interfaces, and refer to
+ them within the ``pyramid.authentication`` and ``pyramid.authorization``
+ API docs.
+
+- Render the function definitions for each exposed interface in
+ ``pyramid.interfaces``.
+
+- Add missing docs reference to
+ ``pyramid.config.Configurator.set_view_mapper`` and refer to it within
+ Hooks chapter section named "Using a View Mapper".
+
Features
--------
diff --git a/LICENSE.txt b/LICENSE.txt
index c2c18cdb5..28824ee3f 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-The majority of the code in repoze.bfg is supplied under this license:
+The majority of the code in Pyramid is supplied under this license:
A copyright notice accompanies this license document that identifies
the copyright holders.
@@ -39,7 +39,7 @@ The majority of the code in repoze.bfg is supplied under this license:
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
-Portions of the code in repoze.bfg are supplied under the ZPL (headers
+Portions of the code in Pyramid are supplied under the ZPL (headers
within individiual files indicate that these portions are licensed
under the ZPL):
@@ -98,7 +98,7 @@ under the ZPL):
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
-Portions of the code in repoze.bfg are supplied under the Python Software
+Portions of the code in Pyramid are supplied under the Python Software
Foundation License version 2 (headers within individiual files indicate that
these portions are so licensed):
@@ -151,13 +151,13 @@ these portions are so licensed):
agrees to be bound by the terms and conditions of this License
Agreement.
-The documentation portion of repoze.bfg (the rendered contents of the
+The documentation portion of Pyramid (the rendered contents of the
"docs" directory of a software distribution or checkout) is supplied
under the Creative Commons Attribution-Noncommercial-Share Alike 3.0
United States License as described by
http://creativecommons.org/licenses/by-nc-sa/3.0/us/
-Internationalization Code in ``repoze.bfg.i18n`` is supplied under the
+Internationalization Code in ``pyramid.i18n`` is supplied under the
following license:
Copyright (C) 2007 Edgewall Software
diff --git a/TODO.txt b/TODO.txt
index aa109c93a..cb4d5dad4 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -28,10 +28,14 @@ Should-Have
- translationdir ZCML directive use of ``path_spec`` should maybe die.
- Provide a response_set_cookie method on the request for rendered responses
- that can be used as input to response.set_cookie?
+ that can be used as input to response.set_cookie? Or maybe accessing
+ ``request.response`` creates a Response, and you do
+ ``request.response.set_cookie(...)``.
- Fix message catalog extraction / compilation documentation.
+- Review http://alexmarandon.com/articles/zodb_bfg_pyramid_notes .
+
Nice-to-Have
------------
diff --git a/docs/api/authentication.rst b/docs/api/authentication.rst
index a6d4c1e18..bf7f8f8d5 100644
--- a/docs/api/authentication.rst
+++ b/docs/api/authentication.rst
@@ -18,6 +18,7 @@ Helper Classes
~~~~~~~~~~~~~~
.. autoclass:: AuthTktCookieHelper
+ :members:
diff --git a/docs/api/config.rst b/docs/api/config.rst
index 38f809c7e..2b9d7bcef 100644
--- a/docs/api/config.rst
+++ b/docs/api/config.rst
@@ -74,6 +74,8 @@
.. automethod:: set_renderer_globals_factory
+ .. automethod:: set_view_mapper
+
.. automethod:: testing_securitypolicy
.. automethod:: testing_resources
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index 3ce926230..54afdc830 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -21,21 +21,36 @@ Event-Related Interfaces
Other Interfaces
++++++++++++++++
+ .. autointerface:: IAuthenticationPolicy
+ :members:
+
+ .. autointerface:: IAuthorizationPolicy
+ :members:
+
.. autointerface:: IExceptionResponse
+ :members:
.. autointerface:: IRoute
+ :members:
.. autointerface:: IRoutePregenerator
+ :members:
.. autointerface:: ISession
+ :members:
.. autointerface:: ISessionFactory
+ :members:
.. autointerface:: IRendererInfo
+ :members:
.. autointerface:: ITemplateRenderer
+ :members:
.. autointerface:: IViewMapperFactory
+ :members:
.. autointerface:: IViewMapper
+ :members:
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 227db2f0f..7e3fe0a5c 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -606,9 +606,9 @@ A user might make use of these framework components like so:
config.add_handler('two', '/{action}/{id}', MyController)
serve(config.make_wsgi_app())
-The :meth:`pyramid.config.Configurator.set_default_mapper` method can be used
-to set a *default* view mapper (overriding the superdefault view mapper used
-by Pyramid itself).
+The :meth:`pyramid.config.Configurator.set_view_mapper` method can be used to
+set a *default* view mapper (overriding the superdefault view mapper used by
+Pyramid itself).
A *single* view registration can use a view mapper by passing the mapper as
the ``mapper`` argument to :meth:`~pyramid.config.Configuration.add_view`.
diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst
index ee86eb543..e4480d6d9 100644
--- a/docs/tutorials/wiki/authorization.rst
+++ b/docs/tutorials/wiki/authorization.rst
@@ -7,21 +7,25 @@ edit, and add pages to our wiki. For purposes of demonstration we'll change
our application to allow people whom are members of a *group* named
``group:editors`` to add and edit wiki pages but we'll continue allowing
anyone with access to the server to view pages. :app:`Pyramid` provides
-facilities for *authorization* and *authentication*. We'll make use of both
-features to provide security to our application.
+facilities for :term:`authorization` and :term:`authentication`. We'll make
+use of both features to provide security to our application.
-The source code for this tutorial stage can be browsed via
-`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/
-<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/>`_.
+We will add an :term:`authentication policy` and an
+:term:`authorization policy` to our :term:`application
+registry`, add a ``security.py`` module and give our :term:`root`
+resource an :term:`ACL`.
+Then we will add ``login`` and ``logout`` views, and modify the
+existing views to make them return a ``logged_in`` flag to the
+renderer and add :term:`permission` declarations to their ``view_config``
+decorators.
-Configuring a ``pyramid`` Authentication Policy
---------------------------------------------------
+Finally, we will add a ``login.pt`` template and change the existing
+``view.pt`` and ``edit.pt`` to show a "Logout" link when not logged in.
-For any :app:`Pyramid` application to perform authorization, we need to add a
-``security.py`` module and we'll need to change our :term:`application
-registry` to add an :term:`authentication policy` and a :term:`authorization
-policy`.
+The source code for this tutorial stage can be browsed via
+`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/
+<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/>`_.
Adding Authentication and Authorization Policies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -64,6 +68,43 @@ user and groups sources. Note that the ``editor`` user is a member of the
``group:editors`` group in our dummy group data (the ``GROUPS`` data
structure).
+Giving Our Root Resource an ACL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We need to give our root resource object an :term:`ACL`. This ACL will be
+sufficient to provide enough information to the :app:`Pyramid` security
+machinery to challenge a user who doesn't have appropriate credentials when
+he attempts to invoke the ``add_page`` or ``edit_page`` views.
+
+We need to perform some imports at module scope in our ``models.py`` file:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.security import Allow
+ from pyramid.security import Everyone
+
+Our root resource object is a ``Wiki`` instance. We'll add the following
+line at class scope to our ``Wiki`` class:
+
+.. code-block:: python
+ :linenos:
+
+ __acl__ = [ (Allow, Everyone, 'view'),
+ (Allow, 'group:editors', 'edit') ]
+
+It's only happenstance that we're assigning this ACL at class scope. An ACL
+can be attached to an object *instance* too; this is how "row level security"
+can be achieved in :app:`Pyramid` applications. We actually only need *one*
+ACL for the entire system, however, because our security requirements are
+simple, so this feature is not demonstrated.
+
+Our resulting ``models.py`` file will now look like so:
+
+.. literalinclude:: src/authorization/tutorial/models.py
+ :linenos:
+ :language: python
+
Adding Login and Logout Views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -129,6 +170,38 @@ template. For example:
logged_in = logged_in,
edit_url = edit_url)
+Adding ``permission`` Declarations to our ``view_config`` Decorators
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To protect each of our views with a particular permission, we need to pass a
+``permission`` argument to each of our :class:`pyramid.view.view_config`
+decorators. To do so, within ``views.py``:
+
+- We add ``permission='view'`` to the decorator attached to the
+ ``view_wiki`` and ``view_page`` view functions. This makes the
+ assertion that only users who possess the ``view`` permission
+ against the context resource at the time of the request may
+ invoke these views. We've granted
+ :data:`pyramid.security.Everyone` the view permission at the
+ root model via its ACL, so everyone will be able to invoke the
+ ``view_wiki`` and ``view_page`` views.
+
+- We add ``permission='edit'`` to the decorator attached to the
+ ``add_page`` and ``edit_page`` view functions. This makes the
+ assertion that only users who possess the effective ``edit``
+ permission against the context resource at the time of the
+ request may invoke these views. We've granted the
+ ``group:editors`` principal the ``edit`` permission at the
+ root model via its ACL, so only a user whom is a member of
+ the group named ``group:editors`` will able to invoke the
+ ``add_page`` or ``edit_page`` views. We've likewise given
+ the ``editor`` user membership to this group via the
+ ``security.py`` file by mapping him to the ``group:editors``
+ group in the ``GROUPS`` data structure (``GROUPS
+ = {'editor':['group:editors']}``); the ``groupfinder``
+ function consults the ``GROUPS`` data structure. This means
+ that the ``editor`` user can add and edit pages.
+
Adding the ``login.pt`` Template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -154,92 +227,29 @@ class="app-welcome align-right">`` div:
<a href="${request.application_url}/logout">Logout</a>
</span>
-Giving Our Root Resource an ACL
--------------------------------
-
-We need to give our root resource object an :term:`ACL`. This ACL will be
-sufficient to provide enough information to the :app:`Pyramid` security
-machinery to challenge a user who doesn't have appropriate credentials when
-he attempts to invoke the ``add_page`` or ``edit_page`` views.
+Seeing Our Changes To ``views.py`` and our Templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We need to perform some imports at module scope in our ``models.py`` file:
+Our ``views.py`` module will look something like this when we're done:
-.. code-block:: python
+.. literalinclude:: src/authorization/tutorial/views.py
:linenos:
+ :language: python
- from pyramid.security import Allow
- from pyramid.security import Everyone
-
-Our root resource object is a ``Wiki`` instance. We'll add the following
-line at class scope to our ``Wiki`` class:
+Our ``edit.pt`` template will look something like this when we're done:
-.. code-block:: python
+.. literalinclude:: src/authorization/tutorial/templates/edit.pt
:linenos:
+ :language: xml
- __acl__ = [ (Allow, Everyone, 'view'),
- (Allow, 'group:editors', 'edit') ]
-
-It's only happenstance that we're assigning this ACL at class scope. An ACL
-can be attached to an object *instance* too; this is how "row level security"
-can be achieved in :app:`Pyramid` applications. We actually only need *one*
-ACL for the entire system, however, because our security requirements are
-simple, so this feature is not demonstrated.
-
-Our resulting ``models.py`` file will now look like so:
+Our ``view.pt`` template will look something like this when we're done:
-.. literalinclude:: src/authorization/tutorial/models.py
+.. literalinclude:: src/authorization/tutorial/templates/view.pt
:linenos:
- :language: python
-
-Adding ``permission`` Declarations to our ``view_config`` Decorators
---------------------------------------------------------------------
-
-To protect each of our views with a particular permission, we need to pass a
-``permission`` argument to each of our :class:`pyramid.view.view_config`
-decorators. To do so, within ``views.py``:
-
-- We add ``permission='view'`` to the decorator attached to the ``view_wiki``
- view function. This makes the assertion that only users who possess the
- ``view`` permission against the context resource at the time of the request
- may invoke this view. We've granted :data:`pyramid.security.Everyone` the
- view permission at the root model via its ACL, so everyone will be able to
- invoke the ``view_wiki`` view.
-
-- We add ``permission='view'`` to the decorator attached to the ``view_page``
- view function. This makes the assertion that only users who possess the
- effective ``view`` permission against the context resource at the time of
- the request may invoke this view. We've granted
- :data:`pyramid.security.Everyone` the view permission at the root model via
- its ACL, so everyone will be able to invoke the ``view_page`` view.
-
-- We add ``permission='edit'`` to the decorator attached to the ``add_page``
- view function. This makes the assertion that only users who possess the
- effective ``edit`` permission against the context resource at the time of
- the request may invoke this view. We've granted the ``group:editors``
- principal the ``edit`` permission at the root model via its ACL, so only
- the a user whom is a member of the group named ``group:editors`` will able
- to invoke the ``add_page`` view. We've likewise given the ``editor`` user
- membership to this group via thes ``security.py`` file by mapping him to
- the ``group:editors`` group in the ``GROUPS`` data structure (``GROUPS =
- {'editor':['group:editors']}``); the ``groupfinder`` function consults the
- ``GROUPS`` data structure. This means that the ``editor`` user can add
- pages.
-
-- We add ``permission='edit'`` to the decorator attached to the ``edit_page``
- view function. This makes the assertion that only users who possess the
- effective ``edit`` permission against the context resource at the time of
- the request may invoke this view. We've granted the ``group:editors``
- principal the ``edit`` permission at the root model via its ACL, so only
- the a user whom is a member of the group named ``group:editors`` will able
- to invoke the ``edit_page`` view. We've likewise given the ``editor`` user
- membership to this group via thes ``security.py`` file by mapping him to
- the ``group:editors`` group in the ``GROUPS`` data structure (``GROUPS =
- {'editor':['group:editors']}``); the ``groupfinder`` function consults the
- ``GROUPS`` data structure. This means that the ``editor`` user can edit
- pages.
+ :language: xml
Viewing the Application in a Browser
-------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We can finally examine our application in a browser. The views we'll try are
as follows:
@@ -267,35 +277,7 @@ as follows:
credentials with the username ``editor``, password ``editor`` will
show the edit page form being displayed.
-Seeing Our Changes To ``views.py`` and our Templates
-----------------------------------------------------
-
-Our ``views.py`` module will look something like this when we're done:
-
-.. literalinclude:: src/authorization/tutorial/views.py
- :linenos:
- :language: python
-
-Our ``edit.pt`` template will look something like this when we're done:
-
-.. literalinclude:: src/authorization/tutorial/templates/edit.pt
- :linenos:
- :language: xml
-
-Our ``view.pt`` template will look something like this when we're done:
-
-.. literalinclude:: src/authorization/tutorial/templates/view.pt
- :linenos:
- :language: xml
-
-Revisiting the Application
----------------------------
-
-When we revisit the application in a browser, and log in (as a result
-of hitting an edit or add page and submitting the login form with the
-``editor`` credentials), we'll see a Logout link in the upper right
-hand corner. When we click it, we're logged out, and redirected back
-to the front page.
-
-
-
+- After logging in (as a result of hitting an edit or add page and
+ submitting the login form with the ``editor`` credentials), we'll see
+ a Logout link in the upper right hand corner. When we click it,
+ we're logged out, and redirected back to the front page.
diff --git a/pyramid/authentication.py b/pyramid/authentication.py
index 46593c8a4..a6c74e549 100644
--- a/pyramid/authentication.py
+++ b/pyramid/authentication.py
@@ -61,12 +61,15 @@ class RepozeWho1AuthenticationPolicy(CallbackAuthenticationPolicy):
``callback``
- Default: ``None``. A callback passed the :mod:`repoze.who`
- identity and the :term:`request`, expected to return ``None``
- if the user represented by the identity doesn't exist or a
- sequence of principal identifiers (possibly empty) if the user
- does exist. If ``callback`` is None, the userid will be
- assumed to exist with no principals.
+ Default: ``None``. A callback passed the :mod:`repoze.who` identity
+ and the :term:`request`, expected to return ``None`` if the user
+ represented by the identity doesn't exist or a sequence of principal
+ identifiers (possibly empty) representing groups if the user does
+ exist. If ``callback`` is None, the userid will be assumed to exist
+ with no group principals.
+
+ Objects of this class implement the interface described by
+ :class:`pyramid.interfaces.IAuthenticationPolicy`.
"""
implements(IAuthenticationPolicy)
@@ -146,10 +149,13 @@ class RemoteUserAuthenticationPolicy(CallbackAuthenticationPolicy):
``callback``
Default: ``None``. A callback passed the userid and the request,
- expected to return None if the userid doesn't exist or a sequence
- of principal identifiers (possibly empty) if the user does exist.
- If ``callback`` is None, the userid will be assumed to exist with no
- principals.
+ expected to return None if the userid doesn't exist or a sequence of
+ principal identifiers (possibly empty) representing groups if the
+ user does exist. If ``callback`` is None, the userid will be assumed
+ to exist with no group principals.
+
+ Objects of this class implement the interface described by
+ :class:`pyramid.interfaces.IAuthenticationPolicy`.
"""
implements(IAuthenticationPolicy)
@@ -257,6 +263,9 @@ class AuthTktAuthenticationPolicy(CallbackAuthenticationPolicy):
Default: ``True``. An auth_tkt cookie will be generated for the
wildcard domain.
Optional.
+
+ Objects of this class implement the interface described by
+ :class:`pyramid.interfaces.IAuthenticationPolicy`.
"""
implements(IAuthenticationPolicy)
def __init__(self,
diff --git a/pyramid/authorization.py b/pyramid/authorization.py
index f27369172..ac8f195f2 100644
--- a/pyramid/authorization.py
+++ b/pyramid/authorization.py
@@ -55,6 +55,9 @@ class ACLAuthorizationPolicy(object):
is cleared for all principals encountered in previous ACLs. The
walking process ends after we've processed the any ACL directly
attached to ``context``; a set of principals is returned.
+
+ Objects of this class implement the
+ :class:`pyramid.interfaces.IAuthorizationPolicy` interface.
"""
implements(IAuthorizationPolicy)
@@ -115,18 +118,18 @@ class ACLAuthorizationPolicy(object):
for ace_action, ace_principal, ace_permissions in acl:
if not hasattr(ace_permissions, '__iter__'):
ace_permissions = [ace_permissions]
- if ace_action == Allow and permission in ace_permissions:
+ if (ace_action == Allow) and (permission in ace_permissions):
if not ace_principal in denied_here:
allowed_here.add(ace_principal)
- if ace_action == Deny and permission in ace_permissions:
- denied_here.add(ace_principal)
- if ace_principal == Everyone:
- # clear the entire allowed set, as we've hit a
- # deny of Everyone ala (Deny, Everyone, ALL)
- allowed = set()
- break
- elif ace_principal in allowed:
- allowed.remove(ace_principal)
+ if (ace_action == Deny) and (permission in ace_permissions):
+ denied_here.add(ace_principal)
+ if ace_principal == Everyone:
+ # clear the entire allowed set, as we've hit a
+ # deny of Everyone ala (Deny, Everyone, ALL)
+ allowed = set()
+ break
+ elif ace_principal in allowed:
+ allowed.remove(ace_principal)
allowed.update(allowed_here)
diff --git a/pyramid/config.py b/pyramid/config.py
index 11a639286..9fda75daa 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -381,18 +381,6 @@ class Configurator(object):
return deriver(view)
- def _override(self, package, path, override_package, override_prefix,
- PackageOverrides=PackageOverrides):
- pkg_name = package.__name__
- override_pkg_name = override_package.__name__
- override = self.registry.queryUtility(
- IPackageOverrides, name=pkg_name)
- if override is None:
- override = PackageOverrides(package)
- self.registry.registerUtility(override, IPackageOverrides,
- name=pkg_name)
- override.insert(path, override_pkg_name, override_prefix)
-
@action_method
def _set_security_policies(self, authentication, authorization=None):
if (authorization is not None) and (not authentication):
@@ -1698,7 +1686,10 @@ class Configurator(object):
request_iface = self.registry.queryUtility(IRouteRequest, name=name)
if request_iface is None:
- bases = use_global_views and (IRequest,) or ()
+ if use_global_views:
+ bases = (IRequest,)
+ else:
+ bases = ()
request_iface = route_request_iface(name, bases)
self.registry.registerUtility(
request_iface, IRouteRequest, name=name)
@@ -1832,6 +1823,17 @@ class Configurator(object):
self.registry.registerUtility(factory, IRendererFactory, name=name)
self.action((IRendererFactory, name), None)
+ def _override(self, package, path, override_package, override_prefix,
+ PackageOverrides=PackageOverrides):
+ pkg_name = package.__name__
+ override_pkg_name = override_package.__name__
+ override = self.registry.queryUtility(IPackageOverrides, name=pkg_name)
+ if override is None:
+ override = PackageOverrides(package)
+ self.registry.registerUtility(override, IPackageOverrides,
+ name=pkg_name)
+ override.insert(path, override_pkg_name, override_prefix)
+
@action_method
def override_asset(self, to_override, override_with, _override=None):
""" Add a :app:`Pyramid` asset override to the current
@@ -1846,8 +1848,7 @@ class Configurator(object):
See :ref:`assets_chapter` for more
information about asset overrides."""
if to_override == override_with:
- raise ConfigurationError('You cannot override an asset with '
- 'itself')
+ raise ConfigurationError('You cannot override an asset with itself')
package = to_override
path = ''
@@ -1859,19 +1860,22 @@ class Configurator(object):
if ':' in override_with:
override_package, override_prefix = override_with.split(':', 1)
- if path and path.endswith('/'):
- if override_prefix and (not override_prefix.endswith('/')):
- raise ConfigurationError(
- 'A directory cannot be overridden with a file (put a '
- 'slash at the end of override_with if necessary)')
+ # *_isdir = override is package or directory
+ overridden_isdir = path=='' or path.endswith('/')
+ override_isdir = override_prefix=='' or override_prefix.endswith('/')
- if override_prefix and override_prefix.endswith('/'):
- if path and (not path.endswith('/')):
- raise ConfigurationError(
- 'A file cannot be overridden with a directory (put a '
- 'slash at the end of to_override if necessary)')
+ if overridden_isdir and (not override_isdir):
+ raise ConfigurationError(
+ 'A directory cannot be overridden with a file (put a '
+ 'slash at the end of override_with if necessary)')
+
+ if (not overridden_isdir) and override_isdir:
+ raise ConfigurationError(
+ 'A file cannot be overridden with a directory (put a '
+ 'slash at the end of to_override if necessary)')
override = _override or self._override # test jig
+
def register():
__import__(package)
__import__(override_package)
@@ -2085,7 +2089,7 @@ class Configurator(object):
if you had passed a ``default_view_mapper`` argument to the
:class:`pyramid.config.Configurator` constructor.
- See also :ref:`using_an_alternate_view_mapper`.
+ See also :ref:`using_a_view_mapper`.
"""
mapper = self.maybe_dotted(mapper)
self.registry.registerUtility(mapper, IViewMapperFactory)
@@ -2666,10 +2670,8 @@ 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.authn_policy = self.registry.queryUtility(IAuthenticationPolicy)
+ self.authz_policy = self.registry.queryUtility(IAuthorizationPolicy)
self.logger = self.registry.queryUtility(IDebugLogger)
def __call__(self, view):
diff --git a/pyramid/encode.py b/pyramid/encode.py
index 127c405ed..9c3a8f7c7 100644
--- a/pyramid/encode.py
+++ b/pyramid/encode.py
@@ -9,9 +9,6 @@ _must_quote = {}
def url_quote(s, safe=''):
"""quote('abc def') -> 'abc%20def'
- Faster version of Python stdlib urllib.quote which also quotes
- the '/' character.
-
Each part of a URL, e.g. the path info, the query, etc., has a
different set of reserved characters that must be quoted.
@@ -25,10 +22,10 @@ def url_quote(s, safe=''):
but not necessarily in all of them.
Unlike the default version of this function in the Python stdlib,
- by default, the quote function is intended for quoting individual
+ by default, the url_quote function is intended for quoting individual
path segments instead of an already composed path that might have
'/' characters in it. Thus, it *will* encode any '/' character it
- finds in a string.
+ finds in a string. It is also slightly faster than the stdlib version.
"""
cachekey = (safe, always_safe)
try:
@@ -41,7 +38,10 @@ def url_quote(s, safe=''):
safe_map = {}
for i in range(256):
c = chr(i)
- safe_map[c] = (c in safe) and c or ('%%%02X' % i)
+ if c in safe:
+ safe_map[c] = c
+ else:
+ safe_map[c] = '%%%02X' % i
_safemaps[cachekey] = safe_map
res = map(safe_map.__getitem__, s)
return ''.join(res)
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index 0f098f0f6..2364757ab 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -141,17 +141,6 @@ class IViewMapperFactory(Interface):
invocation signatures and response values.
"""
-# internal interfaces
-
-class IRequest(Interface):
- """ Request type interface attached to all request objects """
-
-IRequest.combined = IRequest # for exception view lookups
-
-class IRouteRequest(Interface):
- """ *internal only* interface used as in a utility lookup to find
- route-specific interfaces. Not an API."""
-
class IAuthenticationPolicy(Interface):
""" An object representing a Pyramid authentication policy. """
def authenticated_userid(request):
@@ -179,7 +168,7 @@ class IAuthenticationPolicy(Interface):
""" Return a set of headers suitable for 'remembering' the
principal named ``principal`` when set in a response. An
individual authentication policy and its consumers can decide
- on the composition and meaning of **kw. """
+ on the composition and meaning of ``**kw.`` """
def forget(request):
""" Return a set of headers suitable for 'forgetting' the
@@ -201,6 +190,18 @@ class IAuthorizationPolicy(Interface):
``pyramid.security.principals_allowed_by_permission`` API is
used."""
+
+# internal interfaces
+
+class IRequest(Interface):
+ """ Request type interface attached to all request objects """
+
+IRequest.combined = IRequest # for exception view lookups
+
+class IRouteRequest(Interface):
+ """ *internal only* interface used as in a utility lookup to find
+ route-specific interfaces. Not an API."""
+
class IStaticURLInfo(Interface):
""" A policy for generating URLs to static assets """
def add(name, spec, **extra):
diff --git a/pyramid/paster.py b/pyramid/paster.py
index bc1573fb8..f9f8925d7 100644
--- a/pyramid/paster.py
+++ b/pyramid/paster.py
@@ -10,7 +10,7 @@ from paste.util.template import paste_script_template_renderer
from pyramid.scripting import get_root
class PyramidTemplate(Template):
- def pre(self, command, output_dir, vars): # pragma: no cover
+ def pre(self, command, output_dir, vars):
vars['random_string'] = os.urandom(20).encode('hex')
package_logger = vars['package']
if package_logger == 'root':
@@ -19,9 +19,12 @@ class PyramidTemplate(Template):
vars['package_logger'] = package_logger
return Template.pre(self, command, output_dir, vars)
- def post(self, *arg, **kw): # pragma: no cover
- print 'Welcome to Pyramid. Sorry for the convenience.'
- return Template.post(self, *arg, **kw)
+ def post(self, command, output_dir, vars):
+ self.out('Welcome to Pyramid. Sorry for the convenience.')
+ return Template.post(self, command, output_dir, vars)
+
+ def out(self, msg): # pragma: no cover (replaceable testing hook)
+ print msg
class StarterProjectTemplate(PyramidTemplate):
_template_dir = 'paster_templates/starter'
@@ -88,7 +91,7 @@ class PShellCommand(PCommand):
command will almost certainly fail.
"""
- summary = "Open an interactive shell with a pyramid app loaded"
+ summary = "Open an interactive shell with a Pyramid application loaded"
min_args = 2
max_args = 2
@@ -100,10 +103,11 @@ class PShellCommand(PCommand):
help="Don't use IPython even if it is available")
def command(self, IPShell=_marker):
- if IPShell is _marker:
- try: #pragma no cover
+ # IPShell passed to command method is for testing purposes
+ if IPShell is _marker: # pragma: no cover
+ try:
from IPython.Shell import IPShell
- except ImportError: #pragma no cover
+ except ImportError:
IPShell = None
cprt =('Type "help" for more information. "root" is the Pyramid app '
'root object, "registry" is the Pyramid registry object.')
@@ -113,16 +117,17 @@ class PShellCommand(PCommand):
app = self.get_app(config_file, section_name, loadapp=self.loadapp[0])
root, closer = self.get_root(app)
shell_globals = {'root':root, 'registry':app.registry}
- if IPShell is not None and not self.options.disable_ipython:
+
+ if (IPShell is None) or self.options.disable_ipython:
try:
- shell = IPShell(argv=[], user_ns=shell_globals)
- shell.IP.BANNER = shell.IP.BANNER + '\n\n' + banner
- shell.mainloop()
+ self.interact[0](banner, local=shell_globals)
finally:
closer()
else:
try:
- self.interact[0](banner, local=shell_globals)
+ shell = IPShell(argv=[], user_ns=shell_globals)
+ shell.IP.BANNER = shell.IP.BANNER + '\n\n' + banner
+ shell.mainloop()
finally:
closer()
diff --git a/pyramid/renderers.py b/pyramid/renderers.py
index f58595a2c..c8771709a 100644
--- a/pyramid/renderers.py
+++ b/pyramid/renderers.py
@@ -160,15 +160,30 @@ class ChameleonRendererLookup(object):
self.lock = threading.Lock()
def get_spec(self, name, package):
+ if not package:
+ # if there's no package, we can't do any conversion
+ return name
+
spec = name
- isabs = os.path.isabs(name)
+ isabspath = os.path.isabs(name)
+ colon_in_name = ':' in name
+ isabsspec = colon_in_name and (not isabspath)
+ isrelspec = (not isabsspec) and (not isabspath)
+
+ # if it's already an absolute spec, we don't need to do anything,
+ # but if it's a relative spec or an absolute path, we need to try
+ # to convert it to an absolute spec
+
+ if isrelspec:
+ # convert relative asset spec to absolute asset spec
+ pp = package_path(package)
+ spec = os.path.join(pp, spec)
+ spec = asset_spec_from_abspath(spec, package)
- if (not isabs) and (not ':' in name) and package:
- # relative asset spec
- if not isabs:
- pp = package_path(package)
- spec = os.path.join(pp, spec)
+ elif isabspath:
+ # convert absolute path to absolute asset spec
spec = asset_spec_from_abspath(spec, package)
+
return spec
@property # wait until completely necessary to look up translator
@@ -177,12 +192,16 @@ class ChameleonRendererLookup(object):
@property # wait until completely necessary to look up debug_templates
def debug(self):
- settings = self.registry.settings or {}
+ settings = self.registry.settings
+ if settings is None:
+ return False
return settings.get('debug_templates', False)
@property # wait until completely necessary to look up reload_templates
def auto_reload(self):
- settings = self.registry.settings or {}
+ settings = self.registry.settings
+ if settings is None:
+ return False
return settings.get('reload_templates', False)
def __call__(self, info):
@@ -220,7 +239,7 @@ class ChameleonRendererLookup(object):
raise ValueError(
'Missing template asset: %s (%s)' % (spec, abspath))
renderer = self.impl(abspath, self)
- settings = info.settings or {}
+ settings = info.settings
if not settings.get('reload_assets'):
# cache the template
self.lock.acquire()
@@ -268,7 +287,9 @@ class RendererHelper(object):
@reify
def settings(self):
- settings = self.registry.settings or {}
+ settings = self.registry.settings
+ if settings is None:
+ settings = {}
return settings
@reify
diff --git a/pyramid/static.py b/pyramid/static.py
index 3866126ac..80981f0b8 100644
--- a/pyramid/static.py
+++ b/pyramid/static.py
@@ -37,21 +37,18 @@ class PackageURLParser(StaticURLParser):
filename = request.path_info_pop(environ)
resource = os.path.normcase(os.path.normpath(
self.resource_name + '/' + filename))
- if ( (self.root_resource is not None) and
- (not resource.startswith(self.root_resource)) ):
+ if not resource.startswith(self.root_resource):
# Out of bounds
return self.not_found(environ, start_response)
if not pkg_resources.resource_exists(self.package_name, resource):
return self.not_found(environ, start_response)
if pkg_resources.resource_isdir(self.package_name, resource):
# @@: Cache?
- child_root = (self.root_resource is not None and
- self.root_resource or self.resource_name)
return self.__class__(
- self.package_name, resource, root_resource=child_root,
+ self.package_name, resource, root_resource=self.resource_name,
cache_max_age=self.cache_max_age)(environ, start_response)
- if (environ.get('PATH_INFO')
- and environ.get('PATH_INFO') != '/'): # pragma: no cover
+ pi = environ.get('PATH_INFO')
+ if pi and pi != '/':
return self.error_extra_path(environ, start_response)
full = pkg_resources.resource_filename(self.package_name, resource)
if_none_match = environ.get('HTTP_IF_NONE_MATCH')
diff --git a/pyramid/tests/test_asset.py b/pyramid/tests/test_asset.py
index 260e4994e..4a93b14a0 100644
--- a/pyramid/tests/test_asset.py
+++ b/pyramid/tests/test_asset.py
@@ -73,7 +73,7 @@ class TestOverrideProvider(unittest.TestCase):
resource_name = 'fixtures'
import pyramid.tests
provider = self._makeOne(pyramid.tests)
- result = provider.resource_isdir(resource_name)
+ result = provider.resource_listdir(resource_name)
self.failUnless(result)
def test_get_resource_filename_override_returns_None(self):
@@ -96,9 +96,9 @@ class TestOverrideProvider(unittest.TestCase):
import pyramid.tests
provider = self._makeOne(pyramid.tests)
here = os.path.dirname(os.path.abspath(__file__))
- expected = os.path.join(here, resource_name)
- result = provider.get_resource_filename(None, resource_name)
- self.assertEqual(result, expected)
+ expected = open(os.path.join(here, resource_name)).read()
+ result = provider.get_resource_stream(None, resource_name)
+ self.assertEqual(result.read(), expected)
def test_get_resource_string_override_returns_None(self):
overrides = DummyOverrides(None)
@@ -108,8 +108,8 @@ class TestOverrideProvider(unittest.TestCase):
import pyramid.tests
provider = self._makeOne(pyramid.tests)
here = os.path.dirname(os.path.abspath(__file__))
- expected = os.path.join(here, resource_name)
- result = provider.get_resource_filename(None, resource_name)
+ expected = open(os.path.join(here, resource_name)).read()
+ result = provider.get_resource_string(None, resource_name)
self.assertEqual(result, expected)
def test_has_resource_override_returns_None(self):
@@ -248,6 +248,17 @@ class TestPackageOverrides(unittest.TestCase):
override = po.overrides[0]
self.assertEqual(override.__class__, FileOverride)
+ def test_insert_emptystring(self):
+ # XXX is this a valid case for a directory?
+ from pyramid.resource import DirectoryOverride
+ package = DummyPackage('package')
+ po = self._makeOne(package)
+ po.overrides= [None]
+ po.insert('', 'package', 'bar/')
+ self.assertEqual(len(po.overrides), 2)
+ override = po.overrides[0]
+ self.assertEqual(override.__class__, DirectoryOverride)
+
def test_search_path(self):
overrides = [ DummyOverride(None), DummyOverride(('package', 'name'))]
package = DummyPackage('package')
@@ -266,6 +277,14 @@ class TestPackageOverrides(unittest.TestCase):
here = os.path.dirname(os.path.abspath(__file__))
expected = os.path.join(here, 'test_asset.py')
self.assertEqual(po.get_filename('whatever'), expected)
+
+ def test_get_filename_file_doesnt_exist(self):
+ overrides = [ DummyOverride(None), DummyOverride(
+ ('pyramid.tests', 'wont_exist'))]
+ package = DummyPackage('package')
+ po = self._makeOne(package)
+ po.overrides= overrides
+ self.assertEqual(po.get_filename('whatever'), None)
def test_get_stream(self):
import os
@@ -276,8 +295,17 @@ class TestPackageOverrides(unittest.TestCase):
po.overrides= overrides
here = os.path.dirname(os.path.abspath(__file__))
expected = open(os.path.join(here, 'test_asset.py')).read()
- self.assertEqual(po.get_stream('whatever').read().replace('\r', ''), expected)
+ self.assertEqual(po.get_stream('whatever').read().replace('\r', ''),
+ expected)
+ def test_get_stream_file_doesnt_exist(self):
+ overrides = [ DummyOverride(None), DummyOverride(
+ ('pyramid.tests', 'wont_exist'))]
+ package = DummyPackage('package')
+ po = self._makeOne(package)
+ po.overrides= overrides
+ self.assertEqual(po.get_stream('whatever'), None)
+
def test_get_string(self):
import os
overrides = [ DummyOverride(None), DummyOverride(
@@ -289,6 +317,14 @@ class TestPackageOverrides(unittest.TestCase):
expected = open(os.path.join(here, 'test_asset.py')).read()
self.assertEqual(po.get_string('whatever').replace('\r', ''), expected)
+ def test_get_string_file_doesnt_exist(self):
+ overrides = [ DummyOverride(None), DummyOverride(
+ ('pyramid.tests', 'wont_exist'))]
+ package = DummyPackage('package')
+ po = self._makeOne(package)
+ po.overrides= overrides
+ self.assertEqual(po.get_string('whatever'), None)
+
def test_has_resource(self):
overrides = [ DummyOverride(None), DummyOverride(
('pyramid.tests', 'test_asset.py'))]
@@ -297,6 +333,14 @@ class TestPackageOverrides(unittest.TestCase):
po.overrides= overrides
self.assertEqual(po.has_resource('whatever'), True)
+ def test_has_resource_file_doesnt_exist(self):
+ overrides = [ DummyOverride(None), DummyOverride(
+ ('pyramid.tests', 'wont_exist'))]
+ package = DummyPackage('package')
+ po = self._makeOne(package)
+ po.overrides= overrides
+ self.assertEqual(po.has_resource('whatever'), None)
+
def test_isdir_false(self):
overrides = [ DummyOverride(
('pyramid.tests', 'test_asset.py'))]
@@ -313,6 +357,14 @@ class TestPackageOverrides(unittest.TestCase):
po.overrides= overrides
self.assertEqual(po.isdir('whatever'), True)
+ def test_isdir_doesnt_exist(self):
+ overrides = [ DummyOverride(None), DummyOverride(
+ ('pyramid.tests', 'wont_exist'))]
+ package = DummyPackage('package')
+ po = self._makeOne(package)
+ po.overrides= overrides
+ self.assertEqual(po.isdir('whatever'), None)
+
def test_listdir(self):
overrides = [ DummyOverride(
('pyramid.tests', 'fixtures'))]
@@ -321,6 +373,14 @@ class TestPackageOverrides(unittest.TestCase):
po.overrides= overrides
self.failUnless(po.listdir('whatever'))
+ def test_listdir_doesnt_exist(self):
+ overrides = [ DummyOverride(None), DummyOverride(
+ ('pyramid.tests', 'wont_exist'))]
+ package = DummyPackage('package')
+ po = self._makeOne(package)
+ po.overrides= overrides
+ self.assertEqual(po.listdir('whatever'), None)
+
class TestDirectoryOverride(unittest.TestCase):
def _getTargetClass(self):
from pyramid.asset import DirectoryOverride
diff --git a/pyramid/tests/test_authorization.py b/pyramid/tests/test_authorization.py
index c4b2fb142..ed461e2ba 100644
--- a/pyramid/tests/test_authorization.py
+++ b/pyramid/tests/test_authorization.py
@@ -169,6 +169,29 @@ class TestACLAuthorizationPolicy(unittest.TestCase):
result = sorted(policy.principals_allowed_by_permission(context,'read'))
self.assertEqual(result, [])
+ def test_principals_allowed_by_permission_deny_not_permission_in_acl(self):
+ from pyramid.security import Deny
+ from pyramid.security import Everyone
+ context = DummyContext()
+ acl = [ (Deny, Everyone, 'write') ]
+ context.__acl__ = acl
+ policy = self._makeOne()
+ result = sorted(
+ policy.principals_allowed_by_permission(context, 'read'))
+ self.assertEqual(result, [])
+
+ def test_principals_allowed_by_permission_deny_permission_in_acl(self):
+ from pyramid.security import Deny
+ from pyramid.security import Everyone
+ context = DummyContext()
+ acl = [ (Deny, Everyone, 'read') ]
+ context.__acl__ = acl
+ policy = self._makeOne()
+ result = sorted(
+ policy.principals_allowed_by_permission(context, 'read'))
+ self.assertEqual(result, [])
+
+
class DummyContext:
def __init__(self, *arg, **kw):
self.__dict__.update(kw)
diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py
index 202dce7bf..d2ff65878 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/tests/test_config.py
@@ -1491,6 +1491,16 @@ class ConfiguratorTests(unittest.TestCase):
self.failIfEqual(wrapper, None)
self.assertEqual(wrapper(None, None), 'OK')
+ def test_add_view_with_route_name_deferred_views_already_exist(self):
+ view = lambda *arg: 'OK'
+ config = self._makeOne(autocommit=True)
+ config.registry.deferred_route_views = {'bar':[]}
+ config.add_view(view=view, route_name='foo')
+ self.assertEqual(len(config.registry.deferred_route_views), 2)
+ self.assertEqual(config.registry.deferred_route_views['bar'], [])
+ infos = config.registry.deferred_route_views['foo']
+ self.assertEqual(len(infos), 1)
+
def test_deferred_route_views_retains_custom_predicates(self):
view = lambda *arg: 'OK'
config = self._makeOne(autocommit=True)
@@ -2489,6 +2499,16 @@ class ConfiguratorTests(unittest.TestCase):
config.add_translation_dirs,
'/wont/exist/on/my/system')
+ def test_add_translation_dirs_no_specs(self):
+ from pyramid.interfaces import ITranslationDirectories
+ from pyramid.interfaces import IChameleonTranslate
+ config = self._makeOne()
+ config.add_translation_dirs()
+ self.assertEqual(config.registry.queryUtility(ITranslationDirectories),
+ None)
+ self.assertEqual(config.registry.queryUtility(IChameleonTranslate),
+ None)
+
def test_add_translation_dirs_asset_spec(self):
import os
from pyramid.interfaces import ITranslationDirectories
@@ -2499,6 +2519,18 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(config.registry.getUtility(ITranslationDirectories),
[locale])
+ def test_add_translation_dirs_asset_spec_existing_translation_dirs(self):
+ import os
+ from pyramid.interfaces import ITranslationDirectories
+ config = self._makeOne(autocommit=True)
+ directories = ['abc']
+ config.registry.registerUtility(directories, ITranslationDirectories)
+ config.add_translation_dirs('pyramid.tests.localeapp:locale')
+ here = os.path.dirname(__file__)
+ locale = os.path.join(here, 'localeapp', 'locale')
+ result = config.registry.getUtility(ITranslationDirectories)
+ self.assertEqual(result, [locale, 'abc'])
+
def test_add_translation_dirs_registers_chameleon_translate(self):
from pyramid.interfaces import IChameleonTranslate
from pyramid.threadlocal import manager
@@ -2539,7 +2571,13 @@ class ConfiguratorTests(unittest.TestCase):
self.assertRaises(ConfigurationError, config.override_asset,
'a:foo.pt', 'a:foo/')
- def test_override_asset_success(self):
+ def test_override_asset_file_with_package(self):
+ from pyramid.exceptions import ConfigurationError
+ config = self._makeOne()
+ self.assertRaises(ConfigurationError, config.override_asset,
+ 'a:foo.pt', 'a')
+
+ def test_override_asset_file_with_file(self):
config = self._makeOne(autocommit=True)
override = DummyUnderOverride()
config.override_asset(
@@ -2553,6 +2591,62 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(override.override_package, subpackage)
self.assertEqual(override.override_prefix, 'templates/bar.pt')
+ def test_override_asset_package_with_package(self):
+ config = self._makeOne(autocommit=True)
+ override = DummyUnderOverride()
+ config.override_asset(
+ 'pyramid.tests.fixtureapp',
+ 'pyramid.tests.fixtureapp.subpackage',
+ _override=override)
+ from pyramid.tests import fixtureapp
+ from pyramid.tests.fixtureapp import subpackage
+ self.assertEqual(override.package, fixtureapp)
+ self.assertEqual(override.path, '')
+ self.assertEqual(override.override_package, subpackage)
+ self.assertEqual(override.override_prefix, '')
+
+ def test_override_asset_directory_with_directory(self):
+ config = self._makeOne(autocommit=True)
+ override = DummyUnderOverride()
+ config.override_asset(
+ 'pyramid.tests.fixtureapp:templates/',
+ 'pyramid.tests.fixtureapp.subpackage:templates/',
+ _override=override)
+ from pyramid.tests import fixtureapp
+ from pyramid.tests.fixtureapp import subpackage
+ self.assertEqual(override.package, fixtureapp)
+ self.assertEqual(override.path, 'templates/')
+ self.assertEqual(override.override_package, subpackage)
+ self.assertEqual(override.override_prefix, 'templates/')
+
+ def test_override_asset_directory_with_package(self):
+ config = self._makeOne(autocommit=True)
+ override = DummyUnderOverride()
+ config.override_asset(
+ 'pyramid.tests.fixtureapp:templates/',
+ 'pyramid.tests.fixtureapp.subpackage',
+ _override=override)
+ from pyramid.tests import fixtureapp
+ from pyramid.tests.fixtureapp import subpackage
+ self.assertEqual(override.package, fixtureapp)
+ self.assertEqual(override.path, 'templates/')
+ self.assertEqual(override.override_package, subpackage)
+ self.assertEqual(override.override_prefix, '')
+
+ def test_override_asset_package_with_directory(self):
+ config = self._makeOne(autocommit=True)
+ override = DummyUnderOverride()
+ config.override_asset(
+ 'pyramid.tests.fixtureapp',
+ 'pyramid.tests.fixtureapp.subpackage:templates/',
+ _override=override)
+ from pyramid.tests import fixtureapp
+ from pyramid.tests.fixtureapp import subpackage
+ self.assertEqual(override.package, fixtureapp)
+ self.assertEqual(override.path, '')
+ self.assertEqual(override.override_package, subpackage)
+ self.assertEqual(override.override_prefix, 'templates/')
+
def test_add_renderer(self):
from pyramid.interfaces import IRendererFactory
config = self._makeOne(autocommit=True)
@@ -2816,6 +2910,28 @@ class ConfiguratorTests(unittest.TestCase):
renderer.assert_(bar=2)
renderer.assert_(request=request)
+ def test_testing_add_renderer_twice(self):
+ config = self._makeOne(autocommit=True)
+ renderer1 = config.testing_add_renderer('templates/foo.pt')
+ renderer2 = config.testing_add_renderer('templates/bar.pt')
+ from pyramid.testing import DummyTemplateRenderer
+ self.failUnless(isinstance(renderer1, DummyTemplateRenderer))
+ self.failUnless(isinstance(renderer2, DummyTemplateRenderer))
+ from pyramid.renderers import render_to_response
+ # must provide request to pass in registry (this is a functest)
+ request = DummyRequest()
+ request.registry = config.registry
+ render_to_response(
+ 'templates/foo.pt', {'foo':1, 'bar':2}, request=request)
+ renderer1.assert_(foo=1)
+ renderer1.assert_(bar=2)
+ renderer1.assert_(request=request)
+ render_to_response(
+ 'templates/bar.pt', {'foo':1, 'bar':2}, request=request)
+ renderer2.assert_(foo=1)
+ renderer2.assert_(bar=2)
+ renderer2.assert_(request=request)
+
def test_testing_add_renderer_explicitrenderer(self):
config = self._makeOne(autocommit=True)
class E(Exception): pass
@@ -3351,6 +3467,52 @@ class TestViewDeriver(unittest.TestCase):
"'view_name' against context None): Allowed "
"(no authorization policy in use)")
+ def test_with_debug_authorization_authn_policy_no_authz_policy(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(debug_authorization=True)
+ from pyramid.interfaces import IAuthenticationPolicy
+ policy = DummySecurityPolicy(False)
+ self.config.registry.registerUtility(policy, IAuthenticationPolicy)
+ 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_authz_policy_no_authn_policy(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = dict(debug_authorization=True)
+ from pyramid.interfaces import IAuthorizationPolicy
+ policy = DummySecurityPolicy(False)
+ self.config.registry.registerUtility(policy, IAuthorizationPolicy)
+ 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(
@@ -3394,6 +3556,24 @@ class TestViewDeriver(unittest.TestCase):
"debug_authorization of url url (view name "
"'view_name' against context None): True")
+ def test_debug_auth_permission_authpol_permitted_no_request(self):
+ view = lambda *arg: 'OK'
+ 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)
+ self.assertEqual(result(None, None), 'OK')
+ self.assertEqual(len(logger.messages), 1)
+ self.assertEqual(logger.messages[0],
+ "debug_authorization of url None (view name "
+ "None against context None): True")
+
def test_debug_auth_permission_authpol_denied(self):
from pyramid.exceptions import Forbidden
view = lambda *arg: 'OK'
@@ -3454,6 +3634,40 @@ class TestViewDeriver(unittest.TestCase):
"debug_authorization of url url (view name "
"'view_name' against context None): False")
+ def test_secured_view_authn_policy_no_authz_policy(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = {}
+ from pyramid.interfaces import IAuthenticationPolicy
+ policy = DummySecurityPolicy(False)
+ self.config.registry.registerUtility(policy, IAuthenticationPolicy)
+ 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')
+
+ def test_secured_view_authz_policy_no_authn_policy(self):
+ view = lambda *arg: 'OK'
+ self.config.registry.settings = {}
+ from pyramid.interfaces import IAuthorizationPolicy
+ policy = DummySecurityPolicy(False)
+ self.config.registry.registerUtility(policy, IAuthorizationPolicy)
+ 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')
+
def test_with_predicates_all(self):
view = lambda *arg: 'OK'
predicates = []
diff --git a/pyramid/tests/test_encode.py b/pyramid/tests/test_encode.py
index 8a5530f2d..741a24393 100644
--- a/pyramid/tests/test_encode.py
+++ b/pyramid/tests/test_encode.py
@@ -24,6 +24,11 @@ class UrlEncodeTests(unittest.TestCase):
result = self._callFUT([('a', la), ('b',2)], doseq=True)
self.assertEqual(result, 'a=LaPe%C3%B1a&a=LaPe%C3%B1a&b=2')
+ def test_int_val_multiple(self):
+ s = [1, 2]
+ result = self._callFUT([('a', s)], doseq=True)
+ self.assertEqual(result, 'a=1&a=2')
+
def test_dict(self):
result = self._callFUT({'a':1})
self.assertEqual(result, 'a=1')
diff --git a/pyramid/tests/test_i18n.py b/pyramid/tests/test_i18n.py
index ce36c57c8..3155f0ba1 100644
--- a/pyramid/tests/test_i18n.py
+++ b/pyramid/tests/test_i18n.py
@@ -51,6 +51,20 @@ class TestLocalizer(unittest.TestCase):
'singular')
self.failUnless(localizer.pluralizer)
+ def test_pluralize_pluralizer_already_added(self):
+ translations = DummyTranslations()
+ localizer = self._makeOne(None, translations)
+ def pluralizer(*arg, **kw):
+ return arg, kw
+ localizer.pluralizer = pluralizer
+ result = localizer.pluralize('singular', 'plural', 1, domain='1',
+ mapping={})
+ self.assertEqual(
+ result,
+ (('singular', 'plural', 1), {'domain': '1', 'mapping': {}})
+ )
+ self.failUnless(localizer.pluralizer is pluralizer)
+
class Test_negotiate_locale_name(unittest.TestCase):
def setUp(self):
cleanUp()
@@ -174,6 +188,18 @@ class Test_make_localizer(unittest.TestCase):
self.assertEqual(result.translate('Approve', 'deformsite'),
'Approve')
+ def test_locale_from_mo_mo_isdir(self):
+ import os
+ from pyramid.i18n import Localizer
+ here = os.path.dirname(__file__)
+ localedir = os.path.join(here, 'localeapp', 'locale')
+ localedirs = [localedir]
+ locale_name = 'gb'
+ result = self._callFUT(locale_name, localedirs)
+ self.assertEqual(result.__class__, Localizer)
+ self.assertEqual(result.translate('Approve', 'deformsite'),
+ 'Approve')
+
class Test_get_localizer(unittest.TestCase):
def setUp(self):
cleanUp()
@@ -313,6 +339,15 @@ class TestTranslations(unittest.TestCase):
translations = translations1.add(translations2, merge=False)
return translations
+ def test_load_locales_None(self):
+ import gettext
+ import os
+ here = os.path.dirname(__file__)
+ localedir = os.path.join(here, 'localeapp', 'locale')
+ klass = self._getTargetClass()
+ result = klass.load(localedir, None, domain=None)
+ self.assertEqual(result.__class__, gettext.NullTranslations)
+
def test_load_domain_None(self):
import gettext
import os
@@ -357,6 +392,14 @@ class TestTranslations(unittest.TestCase):
inst.merge(inst2)
self.assertEqual(inst._catalog['a'], 'b')
+ def test_merge_gnutranslations_not_translations(self):
+ import gettext
+ t = gettext.GNUTranslations()
+ t._catalog = {'a':'b'}
+ inst = self._makeOne()
+ inst.merge(t)
+ self.assertEqual(inst._catalog['a'], 'b')
+
def test_add_different_domain_merge_true_notexisting(self):
inst = self._makeOne()
inst2 = self._makeOne()
diff --git a/pyramid/tests/test_paster.py b/pyramid/tests/test_paster.py
index 35349b7c7..07ec4f7b7 100644
--- a/pyramid/tests/test_paster.py
+++ b/pyramid/tests/test_paster.py
@@ -1,5 +1,39 @@
import unittest
+class TestPyramidTemplate(unittest.TestCase):
+ def _getTargetClass(self):
+ from pyramid.paster import PyramidTemplate
+ return PyramidTemplate
+
+ def _makeOne(self, name):
+ cls = self._getTargetClass()
+ return cls(name)
+
+ def test_pre_logger_eq_root(self):
+ tmpl = self._makeOne('name')
+ vars = {'package':'root'}
+ result = tmpl.pre(None, None, vars)
+ self.assertEqual(result, None)
+ self.assertEqual(vars['package_logger'], 'app')
+ self.failUnless(len(vars['random_string']) == 40)
+
+ def test_pre_logger_noteq_root(self):
+ tmpl = self._makeOne('name')
+ vars = {'package':'notroot'}
+ result = tmpl.pre(None, None, vars)
+ self.assertEqual(result, None)
+ self.assertEqual(vars['package_logger'], 'notroot')
+ self.failUnless(len(vars['random_string']) == 40)
+
+ def test_post(self):
+ tmpl = self._makeOne('name')
+ vars = {'package':'root'}
+ L = []
+ tmpl.out = lambda msg: L.append(msg)
+ result = tmpl.post(None, None, vars)
+ self.assertEqual(result, None)
+ self.assertEqual(L, ['Welcome to Pyramid. Sorry for the convenience.'])
+
class TestPShellCommand(unittest.TestCase):
def _getTargetClass(self):
from pyramid.paster import PShellCommand
@@ -8,7 +42,7 @@ class TestPShellCommand(unittest.TestCase):
def _makeOne(self):
return self._getTargetClass()('pshell')
- def test_command_ipython_disabled(self):
+ def test_command_ipshell_is_None_ipython_enabled(self):
command = self._makeOne()
interact = DummyInteractor()
app = DummyApp()
@@ -18,7 +52,7 @@ class TestPShellCommand(unittest.TestCase):
command.args = ('/foo/bar/myapp.ini', 'myapp')
class Options(object): pass
command.options = Options()
- command.options.disable_ipython =True
+ command.options.disable_ipython = False
command.command(IPShell=None)
self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
self.assertEqual(loadapp.section_name, 'myapp')
@@ -32,6 +66,30 @@ class TestPShellCommand(unittest.TestCase):
self.failUnless(interact.banner)
self.assertEqual(len(app.threadlocal_manager.popped), 1)
+ def test_command_ipshell_is_not_None_ipython_disabled(self):
+ command = self._makeOne()
+ interact = DummyInteractor()
+ app = DummyApp()
+ loadapp = DummyLoadApp(app)
+ command.interact = (interact,)
+ command.loadapp = (loadapp,)
+ command.args = ('/foo/bar/myapp.ini', 'myapp')
+ class Options(object): pass
+ command.options = Options()
+ command.options.disable_ipython = True
+ command.command(IPShell='notnone')
+ self.assertEqual(loadapp.config_name, 'config:/foo/bar/myapp.ini')
+ self.assertEqual(loadapp.section_name, 'myapp')
+ self.failUnless(loadapp.relative_to)
+ self.assertEqual(len(app.threadlocal_manager.pushed), 1)
+ pushed = app.threadlocal_manager.pushed[0]
+ self.assertEqual(pushed['registry'], dummy_registry)
+ self.assertEqual(pushed['request'].registry, dummy_registry)
+ self.assertEqual(interact.local, {'root':dummy_root,
+ 'registry':dummy_registry})
+ self.failUnless(interact.banner)
+ self.assertEqual(len(app.threadlocal_manager.popped), 1)
+
def test_command_ipython_enabled(self):
command = self._makeOne()
app = DummyApp()
@@ -133,6 +191,19 @@ class TestPRoutesCommand(unittest.TestCase):
self.assertEqual(result, None)
self.assertEqual(L, [])
+ def test_no_mapper(self):
+ command = self._makeOne()
+ command._get_mapper = lambda *arg:None
+ L = []
+ command.out = L.append
+ app = DummyApp()
+ loadapp = DummyLoadApp(app)
+ command.loadapp = (loadapp,)
+ command.args = ('/foo/bar/myapp.ini', 'myapp')
+ result = command.command()
+ self.assertEqual(result, None)
+ self.assertEqual(L, [])
+
def test_single_route_no_route_registered(self):
command = self._makeOne()
route = DummyRoute('a', '/a')
diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py
index 3edeb0f4c..70c2c620e 100644
--- a/pyramid/tests/test_renderers.py
+++ b/pyramid/tests/test_renderers.py
@@ -10,15 +10,34 @@ class TestTemplateRendererFactory(unittest.TestCase):
def tearDown(self):
cleanUp()
- def _callFUT(self, path, factory):
+ def _callFUT(self, info, impl):
from pyramid.renderers import template_renderer_factory
- return template_renderer_factory(path, factory)
+ return template_renderer_factory(info, impl)
+
+ def test_lookup_found(self):
+ from pyramid.interfaces import IChameleonLookup
+ L = []
+ def dummy(info):
+ L.append(info)
+ return True
+ self.config.registry.registerUtility(dummy, IChameleonLookup,
+ name='abc')
+ class DummyInfo(object):
+ pass
+ info = DummyInfo()
+ info.registry = self.config.registry
+ info.type = 'abc'
+ result = self._callFUT(info, None)
+ self.assertEqual(result, True)
+ self.assertEqual(L, [info])
- def test_abspath_notfound(self):
+ def test_lookup_miss(self):
from pyramid.interfaces import ITemplateRenderer
- abspath = '/wont/exist'
+ import os
+ abspath = os.path.abspath(__file__)
+ renderer = {}
self.config.registry.registerUtility(
- {}, ITemplateRenderer, name=abspath)
+ renderer, ITemplateRenderer, name=abspath)
info = DummyRendererInfo({
'name':abspath,
'package':None,
@@ -26,15 +45,142 @@ class TestTemplateRendererFactory(unittest.TestCase):
'settings':{},
'type':'type',
})
- self.assertRaises(ValueError, self._callFUT, info, None)
+ result = self._callFUT(info, None)
+ self.failUnless(result is renderer)
+
+class TestChameleonRendererLookup(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _makeOne(self, impl):
+ from pyramid.renderers import ChameleonRendererLookup
+ return ChameleonRendererLookup(impl, self.config.registry)
- def test_abspath_alreadyregistered(self):
+ def _registerTemplateRenderer(self, renderer, name):
from pyramid.interfaces import ITemplateRenderer
+ self.config.registry.registerUtility(
+ renderer, ITemplateRenderer, name=name)
+
+ def test_get_spec_not_abspath_no_colon_no_package(self):
+ lookup = self._makeOne(None)
+ result = lookup.get_spec('foo', None)
+ self.assertEqual(result, 'foo')
+
+ def test_get_spec_not_abspath_no_colon_with_package(self):
+ from pyramid import tests
+ lookup = self._makeOne(None)
+ result = lookup.get_spec('foo', tests)
+ self.assertEqual(result, 'pyramid.tests:foo')
+
+ def test_get_spec_not_abspath_with_colon_no_package(self):
+ lookup = self._makeOne(None)
+ result = lookup.get_spec('fudge:foo', None)
+ self.assertEqual(result, 'fudge:foo')
+
+ def test_get_spec_not_abspath_with_colon_with_package(self):
+ from pyramid import tests
+ lookup = self._makeOne(None)
+ result = lookup.get_spec('fudge:foo', tests)
+ self.assertEqual(result, 'fudge:foo')
+
+ def test_get_spec_is_abspath_no_colon_no_package(self):
+ import os
+ lookup = self._makeOne(None)
+ spec = os.path.abspath(__file__)
+ result = lookup.get_spec(spec, None)
+ self.assertEqual(result, spec)
+
+ def test_get_spec_is_abspath_no_colon_with_path_in_package(self):
+ from pyramid import tests
+ import os
+ lookup = self._makeOne(None)
+ f = __file__
+ spec = os.path.abspath(f)
+ result = lookup.get_spec(spec, tests)
+ self.assertEqual(result, 'pyramid.tests:%s' % os.path.split(f)[-1])
+
+ def test_get_spec_is_abspath_no_colon_with_path_outside_package(self):
+ import venusian # used only because it's outside of pyramid.tests
+ import os
+ lookup = self._makeOne(None)
+ f = __file__
+ spec = os.path.abspath(f)
+ result = lookup.get_spec(spec, venusian)
+ self.assertEqual(result, spec)
+
+ def test_get_spec_is_abspath_with_colon_no_package(self):
+ import os
+ lookup = self._makeOne(None)
+ spec = os.path.join(os.path.abspath(__file__), ':foo')
+ result = lookup.get_spec(spec, None)
+ self.assertEqual(result, spec)
+
+ def test_get_spec_is_abspath_with_colon_with_path_in_package(self):
+ from pyramid import tests
+ import os
+ lookup = self._makeOne(None)
+ f = os.path.abspath(__file__)
+ spec = os.path.join(f, ':foo')
+ result = lookup.get_spec(spec, tests)
+ tail = spec.split(os.sep)[-2:]
+ self.assertEqual(result, 'pyramid.tests:%s/%s' % tuple(tail))
+
+ def test_get_spec_is_abspath_with_colon_with_path_outside_package(self):
+ import venusian # used only because it's outside of pyramid.tests
+ import os
+ lookup = self._makeOne(None)
+ spec = os.path.join(os.path.abspath(__file__), ':foo')
+ result = lookup.get_spec(spec, venusian)
+ self.assertEqual(result, spec)
+
+ def test_translate(self):
+ from pyramid.interfaces import IChameleonTranslate
+ def t(): pass
+ self.config.registry.registerUtility(t, IChameleonTranslate)
+ lookup = self._makeOne(None)
+ self.assertEqual(lookup.translate, t)
+
+ def test_debug_settings_None(self):
+ self.config.registry.settings = None
+ lookup = self._makeOne(None)
+ self.assertEqual(lookup.debug, False)
+
+ def test_debug_settings_not_None(self):
+ self.config.registry.settings = {'debug_templates':True}
+ lookup = self._makeOne(None)
+ self.assertEqual(lookup.debug, True)
+
+ def test_auto_reload_settings_None(self):
+ self.config.registry.settings = None
+ lookup = self._makeOne(None)
+ self.assertEqual(lookup.auto_reload, False)
+
+ def test_auto_reload_settings_not_None(self):
+ self.config.registry.settings = {'reload_templates':True}
+ lookup = self._makeOne(None)
+ self.assertEqual(lookup.auto_reload, True)
+
+ def test___call__abspath_path_notexists(self):
+ abspath = '/wont/exist'
+ self._registerTemplateRenderer({}, abspath)
+ info = DummyRendererInfo({
+ 'name':abspath,
+ 'package':None,
+ 'registry':self.config.registry,
+ 'settings':{},
+ 'type':'type',
+ })
+ lookup = self._makeOne(None)
+ self.assertRaises(ValueError, lookup.__call__, info)
+
+ def test___call__abspath_alreadyregistered(self):
import os
abspath = os.path.abspath(__file__)
renderer = {}
- self.config.registry.registerUtility(
- renderer, ITemplateRenderer, name=abspath)
+ self._registerTemplateRenderer(renderer, abspath)
info = DummyRendererInfo({
'name':abspath,
'package':None,
@@ -42,16 +188,15 @@ class TestTemplateRendererFactory(unittest.TestCase):
'settings':{},
'type':'type',
})
- result = self._callFUT(info, None)
+ lookup = self._makeOne(None)
+ result = lookup(info)
self.failUnless(result is renderer)
- def test_abspath_notyetregistered(self):
- from pyramid.interfaces import ITemplateRenderer
+ def test___call__abspath_notyetregistered(self):
import os
abspath = os.path.abspath(__file__)
renderer = {}
- self.config.registry.registerUtility(
- renderer, ITemplateRenderer, name=abspath)
+ factory = DummyFactory(renderer)
info = DummyRendererInfo({
'name':abspath,
'package':None,
@@ -59,15 +204,14 @@ class TestTemplateRendererFactory(unittest.TestCase):
'settings':{},
'type':'type',
})
- result = self._callFUT(info, None)
- self.failUnless(result is renderer)
+ lookup = self._makeOne(factory)
+ result = lookup(info)
+ self.assertEqual(result, renderer)
- def test_relpath_path_registered(self):
+ def test___call__relpath_path_registered(self):
renderer = {}
- from pyramid.interfaces import ITemplateRenderer
- self.config.registry.registerUtility(
- renderer, ITemplateRenderer, name='foo/bar')
spec = 'foo/bar'
+ self._registerTemplateRenderer(renderer, spec)
info = DummyRendererInfo({
'name':spec,
'package':None,
@@ -75,17 +219,15 @@ class TestTemplateRendererFactory(unittest.TestCase):
'settings':{},
'type':'type',
})
- result = self._callFUT(info, None)
+ lookup = self._makeOne(None)
+ result = lookup(info)
self.failUnless(renderer is result)
- def test_relpath_has_package_registered(self):
+ def test___call__relpath_has_package_registered(self):
renderer = {}
- from pyramid.interfaces import ITemplateRenderer
import pyramid.tests
spec = 'bar/baz'
- self.config.registry.registerUtility(
- renderer, ITemplateRenderer,
- name='pyramid.tests:bar/baz')
+ self._registerTemplateRenderer(renderer, 'pyramid.tests:bar/baz')
info = DummyRendererInfo({
'name':spec,
'package':pyramid.tests,
@@ -93,10 +235,11 @@ class TestTemplateRendererFactory(unittest.TestCase):
'settings':{},
'type':'type',
})
- result = self._callFUT(info, None)
+ lookup = self._makeOne(None)
+ result = lookup(info)
self.failUnless(renderer is result)
- def test_spec_notfound(self):
+ def test___call__spec_notfound(self):
spec = 'pyramid.tests:wont/exist'
info = DummyRendererInfo({
'name':spec,
@@ -105,10 +248,10 @@ class TestTemplateRendererFactory(unittest.TestCase):
'settings':{},
'type':'type',
})
- self.assertRaises(ValueError, self._callFUT, info, None)
+ lookup = self._makeOne(None)
+ self.assertRaises(ValueError, lookup.__call__, info)
- def test_spec_alreadyregistered(self):
- from pyramid.interfaces import ITemplateRenderer
+ def test___call__spec_alreadyregistered(self):
from pyramid import tests
module_name = tests.__name__
relpath = 'test_renderers.py'
@@ -121,12 +264,12 @@ class TestTemplateRendererFactory(unittest.TestCase):
'type':'type',
})
renderer = {}
- self.config.registry.registerUtility(
- renderer, ITemplateRenderer, name=spec)
- result = self._callFUT(info, None)
+ self._registerTemplateRenderer(renderer, spec)
+ lookup = self._makeOne(None)
+ result = lookup(info)
self.failUnless(result is renderer)
- def test_spec_notyetregistered(self):
+ def test___call__spec_notyetregistered(self):
import os
from pyramid import tests
module_name = tests.__name__
@@ -141,7 +284,8 @@ class TestTemplateRendererFactory(unittest.TestCase):
'settings':{},
'type':'type',
})
- result = self._callFUT(info, factory)
+ lookup = self._makeOne(factory)
+ result = lookup(info)
self.failUnless(result is renderer)
path = os.path.abspath(__file__).split('$')[0] # jython
if path.endswith('.pyc'): # pragma: no cover
@@ -149,7 +293,7 @@ class TestTemplateRendererFactory(unittest.TestCase):
self.failUnless(factory.path.startswith(path))
self.assertEqual(factory.kw, {})
- def test_reload_assets_true(self):
+ def test___call__reload_assets_true(self):
import pyramid.tests
from pyramid.interfaces import ISettings
from pyramid.interfaces import ITemplateRenderer
@@ -166,13 +310,14 @@ class TestTemplateRendererFactory(unittest.TestCase):
'settings':settings,
'type':'type',
})
- result = self._callFUT(info, factory)
+ lookup = self._makeOne(factory)
+ result = lookup(info)
self.failUnless(result is renderer)
spec = '%s:%s' % ('pyramid.tests', 'test_renderers.py')
self.assertEqual(reg.queryUtility(ITemplateRenderer, name=spec),
None)
- def test_reload_assets_false(self):
+ def test___call__reload_assets_false(self):
import pyramid.tests
from pyramid.interfaces import ITemplateRenderer
settings = {'reload_assets':False}
@@ -187,7 +332,8 @@ class TestTemplateRendererFactory(unittest.TestCase):
'settings':settings,
'type':'type',
})
- result = self._callFUT(info, factory)
+ lookup = self._makeOne(factory)
+ result = lookup(info)
self.failUnless(result is renderer)
spec = '%s:%s' % ('pyramid.tests', 'test_renderers.py')
self.assertNotEqual(reg.queryUtility(ITemplateRenderer, name=spec),
@@ -324,6 +470,18 @@ class TestRendererHelper(unittest.TestCase):
helper = self._makeOne()
verifyObject(IRendererInfo, helper)
+ def test_settings_registry_settings_is_None(self):
+ class Dummy(object):
+ settings = None
+ helper = self._makeOne(registry=Dummy)
+ self.assertEqual(helper.settings, {})
+
+ def test_settings_registry_settings_is_not_None(self):
+ class Dummy(object):
+ settings = {'a':1}
+ helper = self._makeOne(registry=Dummy)
+ self.assertEqual(helper.settings, {'a':1})
+
def _registerRendererFactory(self):
from pyramid.interfaces import IRendererFactory
def renderer(*arg):
@@ -504,6 +662,18 @@ class Test_render(unittest.TestCase):
renderer.assert_(a=1)
renderer.assert_(request=request)
+ def test_it_with_package(self):
+ import pyramid.tests
+ renderer = self.config.testing_add_renderer(
+ 'pyramid.tests:abc/def.pt')
+ renderer.string_response = 'abc'
+ request = testing.DummyRequest()
+ result = self._callFUT('abc/def.pt', dict(a=1), request=request,
+ package=pyramid.tests)
+ self.assertEqual(result, 'abc')
+ renderer.assert_(a=1)
+ renderer.assert_(request=request)
+
class Test_render_to_response(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
@@ -535,7 +705,19 @@ class Test_render_to_response(unittest.TestCase):
self.assertEqual(response.body, 'abc')
renderer.assert_(a=1)
renderer.assert_(request=request)
-
+
+ def test_it_with_package(self):
+ import pyramid.tests
+ renderer = self.config.testing_add_renderer(
+ 'pyramid.tests:abc/def.pt')
+ renderer.string_response = 'abc'
+ request = testing.DummyRequest()
+ response = self._callFUT('abc/def.pt', dict(a=1), request=request,
+ package=pyramid.tests)
+ self.assertEqual(response.body, 'abc')
+ renderer.assert_(a=1)
+ renderer.assert_(request=request)
+
class Test_get_renderer(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
@@ -545,14 +727,21 @@ class Test_get_renderer(unittest.TestCase):
def _callFUT(self, renderer_name, **kw):
from pyramid.renderers import get_renderer
- return get_renderer(renderer_name)
+ return get_renderer(renderer_name, **kw)
- def test_it(self):
+ def test_it_no_package(self):
renderer = self.config.testing_add_renderer(
'pyramid.tests:abc/def.pt')
result = self._callFUT('abc/def.pt')
self.assertEqual(result, renderer)
+ def test_it_with_package(self):
+ import pyramid.tests
+ renderer = self.config.testing_add_renderer(
+ 'pyramid.tests:abc/def.pt')
+ result = self._callFUT('abc/def.pt', package=pyramid.tests)
+ self.assertEqual(result, renderer)
+
class Dummy:
pass
diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py
index c559f58d0..ffda14738 100644
--- a/pyramid/tests/test_router.py
+++ b/pyramid/tests/test_router.py
@@ -112,6 +112,14 @@ class TestRouter(unittest.TestCase):
environ.update(extras)
return environ
+ def test_ctor_registry_has_no_settings(self):
+ self.registry.settings = None
+ router = self._makeOne()
+ self.assertEqual(router.debug_notfound, False)
+ self.assertEqual(router.debug_routematch, False)
+ self.failIf('debug_notfound' in router.__dict__)
+ self.failIf('debug_routematch' in router.__dict__)
+
def test_root_policy(self):
context = DummyContext()
self._registerTraverserFactory(context)
diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py
index acf5a754b..cd3006689 100644
--- a/pyramid/tests/test_static.py
+++ b/pyramid/tests/test_static.py
@@ -92,6 +92,14 @@ class TestPackageURLParser(unittest.TestCase):
body = response[0]
self.failUnless('<html>static</html>' in body)
+ def test_resource_has_extra_path_info(self):
+ environ = self._makeEnviron(PATH_INFO='/static/index.html/more')
+ inst = self._makeOne('pyramid.tests', 'fixtures')
+ sr = DummyStartResponse()
+ response = inst(environ, sr)
+ body = response[0]
+ self.failUnless("The trailing path '/more' is not allowed" in body)
+
def test_resource_is_file_with_cache_max_age(self):
environ = self._makeEnviron(PATH_INFO='/index.html')
inst = self._makeOne('pyramid.tests', 'fixtures/static',
@@ -122,6 +130,15 @@ class TestPackageURLParser(unittest.TestCase):
['Accept-Ranges', 'Content-Length', 'Content-Range',
'Content-Type', 'ETag', 'Last-Modified'])
+ def test_with_root_resource(self):
+ environ = self._makeEnviron(PATH_INFO='/static/index.html')
+ inst = self._makeOne('pyramid.tests', 'fixtures',
+ root_resource='fixtures/static')
+ sr = DummyStartResponse()
+ response = inst(environ, sr)
+ body = response[0]
+ self.failUnless('<html>static</html>' in body)
+
def test_if_none_match(self):
class DummyEq(object):
def __eq__(self, other):
@@ -136,6 +153,18 @@ class TestPackageURLParser(unittest.TestCase):
self.assertEqual(sr.headerlist[0][0], 'ETag')
self.assertEqual(response[0], '')
+ def test_if_none_match_miss(self):
+ class DummyEq(object):
+ def __eq__(self, other):
+ return False
+ dummy_eq = DummyEq()
+ environ = self._makeEnviron(HTTP_IF_NONE_MATCH=dummy_eq)
+ inst = self._makeOne('pyramid.tests', 'fixtures/static')
+ sr = DummyStartResponse()
+ inst(environ, sr)
+ self.assertEqual(len(sr.headerlist), 6)
+ self.assertEqual(sr.status, '200 OK')
+
def test_repr(self):
import os.path
inst = self._makeOne('pyramid.tests', 'fixtures/static')
@@ -258,6 +287,14 @@ class TestStaticURLInfo(unittest.TestCase):
request = DummyRequest()
self.assertRaises(ValueError, inst.generate, 'path', request)
+ def test_generate_registration_miss(self):
+ inst = self._makeOne(None)
+ inst.registrations = [('name', 'spec', False),
+ ('http://example.com/foo/', 'package:path/',True)]
+ request = DummyRequest()
+ result = inst.generate('package:path/abc', request)
+ self.assertEqual(result, 'http://example.com/foo/abc')
+
def test_generate_slash_in_name1(self):
inst = self._makeOne(None)
inst.registrations = [('http://example.com/foo/', 'package:path/',True)]
@@ -333,6 +370,17 @@ class TestStaticURLInfo(unittest.TestCase):
permission='abc')
self.assertEqual(config.kw['view_permission'], 'abc')
+ def test_add_viewname_with_view_permission(self):
+ class Config:
+ def add_route(self, *arg, **kw):
+ self.arg = arg
+ self.kw = kw
+ config = Config()
+ inst = self._makeOne(config)
+ inst.add('view', 'anotherpackage:path', cache_max_age=1,
+ view_permission='abc')
+ self.assertEqual(config.kw['view_permission'], 'abc')
+
class DummyStartResponse:
def __call__(self, status, headerlist, exc_info=None):
self.status = status
diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py
index 5d6028b4f..826fc4290 100644
--- a/pyramid/tests/test_view.py
+++ b/pyramid/tests/test_view.py
@@ -499,6 +499,27 @@ class Test_default_exceptionresponse_view(unittest.TestCase):
result = self._callFUT(context, request)
self.assertEqual(result, 'abc')
+class Test_patch_mimetypes(unittest.TestCase):
+ def _callFUT(self, module):
+ from pyramid.view import init_mimetypes
+ return init_mimetypes(module)
+
+ def test_has_init(self):
+ class DummyMimetypes(object):
+ def init(self):
+ self.initted = True
+ module = DummyMimetypes()
+ result = self._callFUT(module)
+ self.assertEqual(result, True)
+ self.assertEqual(module.initted, True)
+
+ def test_missing_init(self):
+ class DummyMimetypes(object):
+ pass
+ module = DummyMimetypes()
+ result = self._callFUT(module)
+ self.assertEqual(result, False)
+
class ExceptionResponse(Exception):
status = '404 Not Found'
app_iter = ['Not Found']
diff --git a/pyramid/view.py b/pyramid/view.py
index 659a63e1e..b2aaba964 100644
--- a/pyramid/view.py
+++ b/pyramid/view.py
@@ -1,15 +1,4 @@
import mimetypes
-
-# See http://bugs.python.org/issue5853 which is a recursion bug
-# that seems to effect Python 2.6, Python 2.6.1, and 2.6.2 (a fix
-# has been applied on the Python 2 trunk). This workaround should
-# really be in Paste if anywhere, but it's easiest to just do it
-# here and get it over with to avoid needing to deal with any
-# fallout.
-
-if hasattr(mimetypes, 'init'):
- mimetypes.init()
-
import venusian
from zope.interface import providedBy
@@ -24,6 +13,21 @@ from pyramid.renderers import RendererHelper
from pyramid.static import static_view
from pyramid.threadlocal import get_current_registry
+def init_mimetypes(mimetypes):
+ # this is a function so it can be unittested
+ if hasattr(mimetypes, 'init'):
+ mimetypes.init()
+ return True
+ return False
+
+# See http://bugs.python.org/issue5853 which is a recursion bug
+# that seems to effect Python 2.6, Python 2.6.1, and 2.6.2 (a fix
+# has been applied on the Python 2 trunk). This workaround should
+# really be in Paste if anywhere, but it's easiest to just do it
+# here and get it over with to avoid needing to deal with any
+# fallout.
+init_mimetypes(mimetypes)
+
# Nast BW compat hack: dont yet deprecate this (ever?)
class static(static_view): # only subclass for purposes of autodoc
__doc__ = static_view.__doc__