diff options
| author | Chris McDonough <chrism@plope.com> | 2011-04-22 13:42:19 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2011-04-22 13:42:19 -0400 |
| commit | ed7ffe0e2065100f551793b3774656d8bdde0fb0 (patch) | |
| tree | 53637b76d148774c5a7c3b9e103373e33e6c2f9e | |
| parent | c150d77248653172b487326a1059b8c0bc5056e4 (diff) | |
| download | pyramid-ed7ffe0e2065100f551793b3774656d8bdde0fb0.tar.gz pyramid-ed7ffe0e2065100f551793b3774656d8bdde0fb0.tar.bz2 pyramid-ed7ffe0e2065100f551793b3774656d8bdde0fb0.zip | |
- Make sure deprecation warnings aren't raised when tests are run.
- Modify documentation for cross-referencing.
- Use add_view(viewname) syntax rather than add_view(view=viewname)
syntax for normalization.
- Use warnings.warn rather than zope.deprecated in order to make
testing easier.
- Move tests which test deprecated methods of configurator to a
separate test case.
| -rw-r--r-- | CHANGES.txt | 24 | ||||
| -rw-r--r-- | docs/narr/advconfig.rst | 12 | ||||
| -rw-r--r-- | docs/narr/hybrid.rst | 39 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 117 | ||||
| -rw-r--r-- | docs/narr/viewconfig.rst | 4 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/basiclayout.rst | 19 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/definingviews.rst | 6 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/src/authorization/tutorial/__init__.py | 10 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py | 2 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/src/models/tutorial/__init__.py | 2 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/src/views/tutorial/__init__.py | 8 | ||||
| -rw-r--r-- | pyramid/config.py | 67 | ||||
| -rw-r--r-- | pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl | 4 | ||||
| -rw-r--r-- | pyramid/tests/ccbugapp/__init__.py | 11 | ||||
| -rw-r--r-- | pyramid/tests/exceptionviewapp/__init__.py | 26 | ||||
| -rw-r--r-- | pyramid/tests/hybridapp/__init__.py | 3 | ||||
| -rw-r--r-- | pyramid/tests/restbugapp/__init__.py | 23 | ||||
| -rw-r--r-- | pyramid/tests/test_config.py | 367 |
18 files changed, 427 insertions, 317 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index c0108ff3a..9e967e5c5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -162,6 +162,30 @@ Deprecations ``request.response_content_type = 'abc'`` should be changed to ``request.response.content_type = 'abc'``). +- Passing view-related parameters to + ``pyramid.config.Configurator.add_route`` is now deprecated. Previously, a + view was permitted to be connected to a route using a set of ``view*`` + parameters passed to the ``add_route`` method of the Configurator. This + was a shorthand which replaced the need to perform a subsequent call to + ``add_view``. For example, it was valid (and often recommended) to do:: + + config.add_route('home', '/', view='mypackage.views.myview', + view_renderer='some/renderer.pt') + + Passing ``view*`` arguments to ``add_route`` is now deprecated in favor of + connecting a view to a predefined route via ``Configurator.add_view`` using + the route's ``route_name`` parameter. As a result, the above example + should now be spelled:: + + config.add_route('home', '/') + config.add_view('mypackage.views.myview', route_name='home') + renderer='some/renderer.pt') + + This deprecation was done to reduce confusion observed in IRC, as well as + to (eventually) reduce documentation burden. A deprecation warning is now + issued when any view-related parameter is passed to + ``Configurator.add_route``. + Behavior Changes ---------------- diff --git a/docs/narr/advconfig.rst b/docs/narr/advconfig.rst index 7ae80155b..5ee554284 100644 --- a/docs/narr/advconfig.rst +++ b/docs/narr/advconfig.rst @@ -295,15 +295,9 @@ These are the methods of the configurator which provide conflict detection: :meth:`~pyramid.config.Configurator.set_locale_negotiator` and :meth:`~pyramid.config.Configurator.set_default_permission`. -Some other methods of the configurator also indirectly provide conflict -detection, because they're implemented in terms of conflict-aware methods: - -- :meth:`~pyramid.config.Configurator.add_route` does a second type of - conflict detection when a ``view`` parameter is passed (it calls - ``add_view``). This behavior has been deprecated in :app:`Pyramid` 1.1. - -- :meth:`~pyramid.config.Configurator.static_view`, a frontend for - ``add_route`` and ``add_view``. +:meth:`~pyramid.config.Configurator.add_static_view` also indirectly +provides conflict detection, because it's implemented in terms of the +conflict-aware ``add_route`` and ``add_view`` methods. .. _including_configuration: diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index d66ad59df..f8ed743fb 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -33,7 +33,7 @@ URL Dispatch Only ~~~~~~~~~~~~~~~~~ An application that uses :term:`url dispatch` exclusively to map URLs to code -will often have statements like this within your application startup +will often have statements like this within application startup configuration: .. code-block:: python @@ -47,9 +47,14 @@ configuration: config.add_view('myproject.views.foobar', route_name='foobar') config.add_view('myproject.views.bazbuz', route_name='bazbuz') -Each :term:`route` corresponds to one or more view callables, -and when that route is matched during a request, :term:`view lookup` is used -to match the request to one of the view callables. +Each :term:`route` corresponds to one or more view callables. Each view +callable is associated with a route by passing a ``route_name`` parameter +that matches its name during a call to +:meth:`~pyramid.config.Configurator.add_view`. When a route is matched +during a request, :term:`view lookup` is used to match the request to its +associated view callable. The presence of calls to +:meth:`~pyramid.config.Configurator.add_route` signify that an application is +using URL dispatch. Traversal Only ~~~~~~~~~~~~~~ @@ -423,13 +428,11 @@ attribute. Using ``*subpath`` in a Route Pattern ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -There are certain extremely rare cases when you'd like to influence -the traversal :term:`subpath` when a route matches without actually -performing traversal. For instance, the -:func:`pyramid.wsgi.wsgiapp2` decorator and the -:class:`pyramid.view.static` helper attempt to compute -``PATH_INFO`` from the request's subpath, so it's useful to be able to -influence this value. +There are certain extremely rare cases when you'd like to influence the +traversal :term:`subpath` when a route matches without actually performing +traversal. For instance, the :func:`pyramid.wsgi.wsgiapp2` decorator and the +:class:`pyramid.view.static` helper attempt to compute ``PATH_INFO`` from the +request's subpath, so it's useful to be able to influence this value. When ``*subpath`` exists in a pattern, no path is actually traversed, but the traversal algorithm will return a :term:`subpath` list implied @@ -455,14 +458,16 @@ application. We'll detail them here. Registering a Default View for a Route That Has a ``view`` Attribute ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. note:: As of :app:`Pyramid` 1.1 this issue is deprecated along with - the ability to add views directly to the :term:`route configuration`. +.. warning:: As of :app:`Pyramid` 1.1 this section is slated to be removed in + a later documentation release because the the ability to add views + directly to the :term:`route configuration` by passing a ``view`` argument + to ``add_route`` has been deprecated. It is an error to provide *both* a ``view`` argument to a :term:`route configuration` *and* a :term:`view configuration` which names a ``route_name`` that has no ``name`` value or the empty ``name`` value. For -example, this pair of declarations will generate a "conflict" error at -startup time. +example, this pair of declarations will generate a conflict error at startup +time. .. code-block:: python :linenos: @@ -490,8 +495,8 @@ Can also be spelled like so: config.add_route('home', '{foo}/{bar}/*traverse') config.add_view('myproject.views.home', route_name='home') -The two spellings are logically equivalent. In fact, the former is -just a syntactical shortcut for the latter. +The two spellings are logically equivalent. In fact, the former is just a +syntactical shortcut for the latter. Binding Extra Views Against a Route Configuration that Doesn't Have a ``*traverse`` Element In Its Pattern ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 2a8052861..4923fd19f 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -54,13 +54,13 @@ Route Configuration ------------------- :term:`Route configuration` is the act of adding a new :term:`route` to an -application. A route has a *pattern*, representing a pattern meant to match +application. A route has a *name*, which acts as an identifier to be used +for URL generation. The name also allows developers to associate a view +configuration with the route. A route also has a *pattern*, meant to match against the ``PATH_INFO`` portion of a URL (the portion following the scheme -and port, e.g. ``/foo/bar`` in the URL ``http://localhost:8080/foo/bar``), -and a *route name*, which is used by developers within a :app:`Pyramid` -application to uniquely identify a particular route when generating a URL. -It also optionally has a ``factory``, a set of :term:`route predicate` -parameters, and a set of view callables. +and port, e.g. ``/foo/bar`` in the URL ``http://localhost:8080/foo/bar``). It +also optionally has a ``factory`` and a set of :term:`route predicate` +attributes. .. index:: single: add_route @@ -89,22 +89,49 @@ example: example ``/prefix/:one/:two``. This style is now deprecated in favor of ``{}`` usage which allows for additional functionality. -.. versionchanged:: 1.1 - Prior to 1.1, views were typically connected to routes using a set of - view parameters on :meth:`pyramid.config.Configurator.add_route`. That - behavior is now deprecated in favor of connecting views to routes using - :meth:`pyramid.config.Configurator.add_view` with the ``route_name`` - parameter. - .. index:: single: route configuration; view callable +.. _add_route_view_config: + Route Configuration That Names a View Callable ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. warning:: This section describes a feature which has been deprecated in - Pyramid 1.1. The recommended way to connect view callables to routes - is via :ref:`config-add-route`. + Pyramid 1.1 and higher. In order to reduce confusion and documentation + burden, passing view-related parameters to + :meth:`~pyramid.config.Configurator.add_route` is deprecated. + + In versions earlier than 1.1, a view was permitted to be connected to a + route using a set of ``view*`` parameters passed to the + :meth:`~pyramid.config.Configurator.add_route`. This was a shorthand + which replaced the need to perform a subsequent call to + :meth:`~pyramid.config.Configurator.add_view` as described in + :ref:`config-add-route`. For example, it was valid (and often recommended) + to do: + + .. code-block:: python + + config.add_route('home', '/', view='mypackage.views.myview', + view_renderer='some/renderer.pt') + + Instead of the equivalent: + + .. code-block:: python + + config.add_route('home', '/') + config.add_view('mypackage.views.myview', route_name='home') + renderer='some/renderer.pt') + + Passing ``view*`` arguments to ``add_route`` as shown in the first + example above is now deprecated in favor of connecting a view to a + predefined route via :meth:`~pyramid.config.Configurator.add_view` using + the route's ``route_name`` parameter, as shown in the second example + above. + + A deprecation warning is now issued when any view-related parameter is + passed to ``Configurator.add_route``. The recommended way to associate a + view with a route is documented in :ref:`config-add-route`. When a route configuration declaration names a ``view`` attribute, the value of the attribute will reference a :term:`view callable`. This view callable @@ -139,6 +166,9 @@ When a route configuration names a ``view`` attribute, the :term:`view callable` named as that ``view`` attribute will always be found and invoked when the associated route pattern matches during a request. +See :ref:`add_route_view_related_api` for a description of view-related +arguments to ``add_route``. + .. index:: single: route path pattern syntax @@ -377,7 +407,8 @@ resource of the view callable ultimately found via :term:`view lookup`. .. code-block:: python :linenos: - config.add_route('abc', '/abc', factory='myproject.resources.root_factory') + config.add_route('abc', '/abc', + factory='myproject.resources.root_factory') config.add_view('myproject.views.theview', route_name='abc') The factory can either be a Python object or a :term:`dotted Python name` (a @@ -410,7 +441,7 @@ process. Examples of route predicate arguments are ``pattern``, ``xhr``, and Other arguments are view configuration related arguments. These only have an effect when the route configuration names a ``view``. These arguments have -been deprecated as of :app:`Pyramid` 1.1. +been deprecated as of :app:`Pyramid` 1.1 (see :ref:`add_route_view_config`). Other arguments are ``name`` and ``factory``. These arguments represent neither predicates nor view configuration information. @@ -562,8 +593,8 @@ If any route matches, the route matching process stops. The :term:`request` is decorated with a special :term:`interface` which describes it as a "route request", the :term:`context` resource is generated, and the context and the resulting request are handed off to :term:`view lookup`. During view lookup, -if any ``view`` was provided within the matched route configuration, the -:term:`view callable` it points to is called. +if a :term:`view callable` associated with the matched route is found, that +view is called. When a route configuration is declared, it may contain :term:`route predicate` arguments. All route predicates associated with a route @@ -754,34 +785,6 @@ request in its ``__init__``. For example: In a more complicated application, this root factory might be a class representing a :term:`SQLAlchemy` model. -Example 4 -~~~~~~~~~ - -It is possible to create a route declaration without a ``view`` attribute, -but associate the route with a :term:`view callable` using a ``view`` -declaration. - -.. code-block:: python - :linenos: - - config.add_route('idea', 'site/{id}') - config.add_view(route_name='idea', view='mypackage.views.site_view') - -This set of configuration parameters creates a configuration completely -equivalent to this example provided in :ref:`urldispatch_example1`: - -.. code-block:: python - :linenos: - - config.add_route('idea', 'site/{id}', view='mypackage.views.site_view') - -In fact, the spelling which names a ``view`` attribute is just syntactic -sugar for the more verbose spelling which contains separate view and route -registrations. - -More uses for this style of associating views with routes are explored in -:ref:`hybrid_chapter`. - .. index:: single: matching the root URL single: root url (matching) @@ -884,8 +887,8 @@ the application's startup configuration, adding the following stanza: .. code-block:: python :linenos: - config.add_view(context='pyramid.exceptions.NotFound', - view='pyramid.view.append_slash_notfound_view') + config.add_view('pyramid.view.append_slash_notfound_view', + context='pyramid.exceptions.NotFound') See :ref:`view_module` and :ref:`changing_the_notfound_view` for more information about the slash-appending not found view and for a more general @@ -1083,25 +1086,25 @@ is executed. Route View Callable Registration and Lookup Details --------------------------------------------------- -When a request enters the system which matches the pattern of the route, -the result is simple: the view callable associated with the route is invoked -with the request that caused the invocation. +When a request enters the system which matches the pattern of the route, the +usual result is simple: the view callable associated with the route is +invoked with the request that caused the invocation. For most usage, you needn't understand more than this; how it works is an implementation detail. In the interest of completeness, however, we'll explain how it *does* work in the this section. You can skip it if you're uninterested. -When a ``view`` is attached to a route configuration, :app:`Pyramid` ensures -that a :term:`view configuration` is registered that will always be found when -the route pattern is matched during a request. To do so: +When a view is associated with a route configuration, :app:`Pyramid` ensures +that a :term:`view configuration` is registered that will always be found +when the route pattern is matched during a request. To do so: - A special route-specific :term:`interface` is created at startup time for each route configuration declaration. -- When a route configuration declaration mentions a ``view`` attribute, a +- When an ``add_view`` statement mentions a ``route name`` attribute, a :term:`view configuration` is registered at startup time. This view - configuration uses the route-specific interface as a :term:`request` type. + configuration uses a route-specific interface as a :term:`request` type. - At runtime, when a request causes any route to match, the :term:`request` object is decorated with the route-specific interface. diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst index 7ee8e3fe5..743cc016e 100644 --- a/docs/narr/viewconfig.rst +++ b/docs/narr/viewconfig.rst @@ -59,8 +59,8 @@ View configuration is performed in one of these ways: - By specifying a view within a :term:`route configuration`. View configuration via a route configuration is performed by using the :meth:`pyramid.config.Configurator.add_route` method, passing a ``view`` - argument specifying a view callable. This method is deprecated as of - :app:`Pyramid` 1.1. + argument specifying a view callable. This pattern of view configuration is + deprecated as of :app:`Pyramid` 1.1. .. note:: A package named ``pyramid_handlers`` (available from PyPI) provides an analogue of :term:`Pylons` -style "controllers", which are a special diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst index bb39a686d..82e112c64 100644 --- a/docs/tutorials/wiki2/basiclayout.rst +++ b/docs/tutorials/wiki2/basiclayout.rst @@ -85,9 +85,9 @@ used when the URL is ``/``: :language: py Since this route has a ``pattern`` equalling ``/`` it is the route that will -be called when the URL ``/`` is visted, e.g. ``http://localhost:6543/``. +be matched when the URL ``/`` is visted, e.g. ``http://localhost:6543/``. -Mapping the ``home`` route to code is done by registering a ``view``. You will +Mapping the ``home`` route to code is done by registering a view. You will use :meth:`pyramid.config.Configurator.add_view` in :term:`URL dispatch` to register views for the routes, mapping your patterns to code: @@ -95,13 +95,14 @@ register views for the routes, mapping your patterns to code: :lines: 14 :language: py -The ``view`` argument of ``tutorial.views.my_view`` is the dotted name to a -*function* we write (generated by the ``pyramid_routesalchemy`` scaffold) that -is given a ``request`` object and which returns a response or a dictionary. -This view also names a ``renderer``, which is a template which lives in the -``templates`` subdirectory of the package. When the ``tutorial.views.my_view`` -view returns a dictionary, a :term:`renderer` will use this template to create -a response. +The first positional ``add_view`` argument ``tutorial.views.my_view`` is the +dotted name to a *function* we write (generated by the +``pyramid_routesalchemy`` scaffold) that is given a ``request`` object and +which returns a response or a dictionary. This view also names a +``renderer``, which is a template which lives in the ``templates`` +subdirectory of the package. When the ``tutorial.views.my_view`` view +returns a dictionary, a :term:`renderer` will use this template to create a +response. This Finally, we use the :meth:`pyramid.config.Configurator.make_wsgi_app` method to return a :term:`WSGI` application: diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst index f59f75702..832f90b92 100644 --- a/docs/tutorials/wiki2/definingviews.rst +++ b/docs/tutorials/wiki2/definingviews.rst @@ -282,8 +282,7 @@ these declarations is very important. ``route`` declarations are matched in the order they're found in the ``__init__.py`` file. #. Add a declaration which maps the pattern ``/`` (signifying the root URL) - to the route named ``view_wiki``. This is the :term:`default view` for the - wiki. + to the route named ``view_wiki``. #. Add a declaration which maps the pattern ``/{pagename}`` to the route named ``view_page``. This is the regular view for a page. @@ -299,7 +298,8 @@ to handle the processing and rendering that needs to happen when each route is requested. #. Add a declaration which maps the ``view_wiki`` route to the view named - ``view_wiki`` in our ``views.py`` file. + ``view_wiki`` in our ``views.py`` file. This is the :term:`default view` + for the wiki. #. Add a declaration which maps the ``view_page`` route to the view named ``view_page`` in our ``views.py`` file. diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index 7da99775c..e8baa568c 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -29,14 +29,14 @@ def main(global_config, **settings): config.add_route('edit_page', '/{pagename}/edit_page') config.add_route('view_wiki', '/') - config.add_view(route_name='login', view='tutorial.login.login', + config.add_view('tutorial.login.login', route_name='login', renderer='tutorial:templates/login.pt') - config.add_view(route_name='logout', view='tutorial.login.logout') - config.add_view(route_name='view_page', view='tutorial.views.view_page', + config.add_view('tutorial.login.logout', route_name='logout') + config.add_view('tutorial.views.view_page', route_name='view_page', renderer='tutorial:templates/view.pt') - config.add_view(route_name='add_page', view='tutorial.views.add_page', + config.add_view('tutorial.views.add_page', route_name='add_page', renderer='tutorial:templates/edit.pt', permission='edit') - config.add_view(route_name='edit_page', view='tutorial.views.edit_page', + config.add_view('tutorial.views.edit_page', route_name='edit_page', renderer='tutorial:templates/edit.pt', permission='edit') config.add_view('tutorial.login.login', context='pyramid.exceptions.Forbidden', diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py index 43fd7f0fe..c74f07652 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py @@ -11,7 +11,7 @@ def main(global_config, **settings): config = Configurator(settings=settings) config.add_static_view('static', 'tutorial:static') config.add_route('home', '/') - config.add_view(route_name='home', view='tutorial.views.my_view', + config.add_view('tutorial.views.my_view', route_name='home', renderer='templates/mytemplate.pt') return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py index ff46bbf8f..ecc41ca9f 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py @@ -11,6 +11,6 @@ def main(global_config, **settings): config = Configurator(settings=settings) config.add_static_view('static', 'tutorial:static') config.add_route('home', '/') - config.add_view(route_name='home', view='tutorial.views.my_view', + config.add_view('tutorial.views.my_view', route_name='home', renderer='templates/mytemplate.pt') return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py index 93abf83e7..ad89c124e 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py @@ -14,12 +14,12 @@ def main(global_config, **settings): config.add_route('view_page', '/{pagename}') config.add_route('add_page', '/add_page/{pagename}') config.add_route('edit_page', '/{pagename}/edit_page') - config.add_view(route_name='view_wiki', view='tutorial.views.view_wiki') - config.add_view(route_name='view_page', view='tutorial.views.view_page', + config.add_view('tutorial.views.view_wiki', route_name='view_wiki') + config.add_view('tutorial.views.view_page', route_name='view_page', renderer='tutorial:templates/view.pt') - config.add_view(route_name='add_page', view='tutorial.views.add_page', + config.add_view('tutorial.views.add_page', route_name='add_page', renderer='tutorial:templates/edit.pt') - config.add_view(route_name='edit_page', view='tutorial.views.edit_page', + config.add_view('tutorial.views.edit_page', route_name='edit_page', renderer='tutorial:templates/edit.pt') return config.make_wsgi_app() diff --git a/pyramid/config.py b/pyramid/config.py index 334e00201..eedb6ad9f 100644 --- a/pyramid/config.py +++ b/pyramid/config.py @@ -4,6 +4,7 @@ import re import sys import types import traceback +import warnings import venusian @@ -13,8 +14,6 @@ from zope.configuration.config import GroupingContextDecorator from zope.configuration.config import ConfigurationMachine from zope.configuration.xmlconfig import registerCommonDirectives -from zope.deprecation import deprecated - from zope.interface import Interface from zope.interface import implementedBy from zope.interface.interfaces import IInterface @@ -1380,7 +1379,6 @@ class Configurator(object): discriminator = tuple(discriminator) self.action(discriminator, register) - @action_method def _add_view_from_route(self, route_name, view, @@ -1418,9 +1416,14 @@ class Configurator(object): raise ConfigurationError( 'view_renderer argument not permitted without ' 'view argument') - _add_view_from_route = deprecated( - _add_view_from_route, 'Use add_view() to add views for a route. No ' - 'longer supported directly from add_route() in pyramid 1.1') + + warnings.warn( + 'Passing view-related arguments to add_route() is deprecated as of ' + 'Pyramid 1.1. Use add_view() to associate a view with a route ' + 'instead. See "Deprecations" in "What\'s New in Pyramid 1.1" ' + 'within the general Pyramid documentation for further details.', + DeprecationWarning, + 4) @action_method def add_route(self, @@ -1457,13 +1460,6 @@ class Configurator(object): narrow the circumstances in which a route will be match a request; non-predicate arguments are informational. - .. warning:: View-related arguments have been deprecated in - :app:`Pyramid` 1.1. *Do not use it for new development; - it should only be used to support older code bases which - depend upon it.* Use - :meth:`pyramid.config.Configurator.add_view` to add views - to a route. - Non-Predicate Arguments name @@ -1535,6 +1531,14 @@ class Configurator(object): by applications, it is meant to be hooked by frameworks that use :app:`Pyramid` as a base. + use_global_views + + When a request matches this route, and view lookup cannot + find a view which has a ``route_name`` predicate argument + that matches the route, try to fall back to using a view + that otherwise matches the context, request, and view name + (but which does not match the route_name predicate). + Predicate Arguments pattern @@ -1641,12 +1645,21 @@ class Configurator(object): :ref:`custom_route_predicates` for more information about ``info``. + .. _add_route_view_related_api: + View-Related Arguments + .. warning:: The arguments described below have been deprecated as of + :app:`Pyramid` 1.1. *Do not use these for new development; they + should only be used to support older code bases which depend upon + them.* Use a separate call to + :meth:`pyramid.config.Configurator.add_view` to associate a view + with a route. + view - .. warning:: Deprecated in favor of - :meth:`pyramid.config.Configurator.add_view`. + .. warning:: Deprecated as of :app:`Pyramid` 1.1; see + :ref:`add_route_view_related_api`. A Python object or :term:`dotted Python name` to the same object that will be used as a view callable when this route @@ -1654,8 +1667,8 @@ class Configurator(object): view_context - .. warning:: Deprecated in favor of - :meth:`pyramid.config.Configurator.add_view`. + .. warning:: Deprecated as of :app:`Pyramid` 1.1; see + :ref:`add_route_view_related_api`. A class or an :term:`interface` or :term:`dotted Python name` to the same object which the :term:`context` of the @@ -1671,8 +1684,8 @@ class Configurator(object): view_permission - .. warning:: Deprecated in favor of - :meth:`pyramid.config.Configurator.add_view`. + .. warning:: Deprecated as of :app:`Pyramid` 1.1; see + :ref:`add_route_view_related_api`. The permission name required to invoke the view associated with this route. e.g. ``edit``. (see @@ -1686,8 +1699,8 @@ class Configurator(object): view_renderer - .. warning:: Deprecated in favor of - :meth:`pyramid.config.Configurator.add_view`. + .. warning:: Deprecated as of :app:`Pyramid` 1.1; see + :ref:`add_route_view_related_api`. This is either a single string term (e.g. ``json``) or a string implying a path or :term:`asset specification` @@ -1711,8 +1724,8 @@ class Configurator(object): view_attr - .. warning:: Deprecated in favor of - :meth:`pyramid.config.Configurator.add_view`. + .. warning:: Deprecated as of :app:`Pyramid` 1.1; see + :ref:`add_route_view_related_api`. The view machinery defaults to using the ``__call__`` method of the view callable (or the function itself, if the view @@ -1728,14 +1741,6 @@ class Configurator(object): If the ``view`` argument is not provided, this argument has no effect. - use_global_views - - When a request matches this route, and view lookup cannot - find a view which has a ``route_name`` predicate argument - that matches the route, try to fall back to using a view - that otherwise matches the context, request, and view name - (but which does not match the route_name predicate). - """ # these are route predicates; if they do not match, the next route # in the routelist will be tried diff --git a/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl index aea2393d6..f5e3a0630 100644 --- a/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl @@ -11,8 +11,8 @@ def main(global_config, **settings): config = Configurator(settings=settings) config.add_static_view('static', '{{package}}:static') config.add_route('home', '/') - config.add_view(route_name='home', - view='{{package}}.views.my_view', + config.add_view('{{package}}.views.my_view', + route_name='home', renderer='templates/mytemplate.pt') return config.make_wsgi_app() diff --git a/pyramid/tests/ccbugapp/__init__.py b/pyramid/tests/ccbugapp/__init__.py index 6c2eb6ecf..ad6387a75 100644 --- a/pyramid/tests/ccbugapp/__init__.py +++ b/pyramid/tests/ccbugapp/__init__.py @@ -1,7 +1,8 @@ def includeme(config): - config.add_route('rdf', - 'licenses/:license_code/:license_version/rdf', - '.views.rdf_view') + config.add_route('rdf', 'licenses/:license_code/:license_version/rdf') config.add_route('juri', - 'licenses/:license_code/:license_version/:jurisdiction', - '.views.juri_view') + 'licenses/:license_code/:license_version/:jurisdiction') + config.add_view('.views.rdf_view', route_name='rdf') + config.add_view('.views.juri_view', route_name='juri') + + diff --git a/pyramid/tests/exceptionviewapp/__init__.py b/pyramid/tests/exceptionviewapp/__init__.py index cf69227cd..f169e0cd5 100644 --- a/pyramid/tests/exceptionviewapp/__init__.py +++ b/pyramid/tests/exceptionviewapp/__init__.py @@ -1,21 +1,23 @@ def includeme(config): + config.add_route('route_raise_exception', 'route_raise_exception') + config.add_route('route_raise_exception2', 'route_raise_exception2', + factory='.models.route_factory') + config.add_route('route_raise_exception3', 'route_raise_exception3', + factory='.models.route_factory2') + config.add_route('route_raise_exception4', 'route_raise_exception4') config.add_view('.views.maybe') config.add_view('.views.no', context='.models.NotAnException') config.add_view('.views.yes', context=".models.AnException") config.add_view('.views.raise_exception', name='raise_exception') - config.add_route('route_raise_exception', 'route_raise_exception', - view='.views.raise_exception') - config.add_route('route_raise_exception2', - 'route_raise_exception2', - view='.views.raise_exception', - factory='.models.route_factory') - config.add_route('route_raise_exception3', - 'route_raise_exception3', - view='.views.raise_exception', - factory='.models.route_factory2') + config.add_view('.views.raise_exception', + route_name='route_raise_exception') + config.add_view('.views.raise_exception', + route_name='route_raise_exception2') + config.add_view('.views.raise_exception', + route_name='route_raise_exception3') config.add_view('.views.whoa', context='.models.AnException', route_name='route_raise_exception3') - config.add_route('route_raise_exception4', 'route_raise_exception4', - view='.views.raise_exception') + config.add_view('.views.raise_exception', + route_name='route_raise_exception4') config.add_view('.views.whoa', context='.models.AnException', route_name='route_raise_exception4') diff --git a/pyramid/tests/hybridapp/__init__.py b/pyramid/tests/hybridapp/__init__.py index 5b51e3d1e..1cc2dde83 100644 --- a/pyramid/tests/hybridapp/__init__.py +++ b/pyramid/tests/hybridapp/__init__.py @@ -1,6 +1,7 @@ def includeme(config): # <!-- we want this view to "win" --> - config.add_route('route', 'abc', view='.views.route_view') + config.add_route('route', 'abc') + config.add_view('.views.route_view', route_name='route') # <!-- .. even though this one has a more specific context --> config.add_view('.views.global_view', context='pyramid.traversal.DefaultRootFactory') diff --git a/pyramid/tests/restbugapp/__init__.py b/pyramid/tests/restbugapp/__init__.py index 461fcce92..9ad79e32e 100644 --- a/pyramid/tests/restbugapp/__init__.py +++ b/pyramid/tests/restbugapp/__init__.py @@ -1,14 +1,15 @@ def includeme(config): config.add_route('gameactions_pet_get_pets', '/pet', - view='.views.PetRESTView', - view_attr='GET', - request_method='GET', - permission='view', - renderer='json') + request_method='GET') config.add_route('gameactions_pet_care_for_pet', '/pet', - view='.views.PetRESTView', - view_attr='POST', - request_method='POST', - permission='view', - renderer='json') - + request_method='POST') + config.add_view('.views.PetRESTView', + route_name='gameactions_pet_get_pets', + attr='GET', + permission='view', + renderer='json') + config.add_view('.views.PetRESTView', + route_name='gameactions_pet_care_for_pet', + attr='POST', + permission='view', + renderer='json') diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py index 5818f248b..578965702 100644 --- a/pyramid/tests/test_config.py +++ b/pyramid/tests/test_config.py @@ -68,6 +68,12 @@ class ConfiguratorTests(unittest.TestCase): request.registry = config.registry return request + def _conflictFunctions(self, e): + conflicts = e._conflicts.values() + for conflict in conflicts: + for confinst in conflict: + yield confinst[2] + def test_ctor_no_registry(self): import sys from pyramid.interfaces import ISettings @@ -1979,129 +1985,6 @@ class ConfiguratorTests(unittest.TestCase): request.accept = ['text/html'] self.assertEqual(predicate(None, request), False) - def test_add_route_with_view(self): - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - - def test_add_route_with_view_context(self): - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_context=IDummy) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, IDummy, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable(config, IOther, request_type) - self.assertEqual(wrapper, None) - - def test_add_route_with_view_exception(self): - from zope.interface import implementedBy - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_context=RuntimeError) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - request_iface=request_type, exception_view=True) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable( - config, ctx_iface=IOther, - request_iface=request_type, exception_view=True) - self.assertEqual(wrapper, None) - - def test_add_route_with_view_for(self): - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_for=IDummy) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, IDummy, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable(config, IOther, request_type) - self.assertEqual(wrapper, None) - - def test_add_route_with_for_(self): - config = self._makeOne(autocommit=True) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, for_=IDummy) - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, IDummy, request_type) - self.assertEqual(wrapper(None, None), 'OK') - self._assertRoute(config, 'name', 'path') - wrapper = self._getViewCallable(config, IOther, request_type) - self.assertEqual(wrapper, None) - - def test_add_route_with_view_renderer(self): - config = self._makeOne(autocommit=True) - self._registerRenderer(config) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, - view_renderer='fixtures/minimal.txt') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.assertEqual(wrapper(None, None).body, 'Hello!') - - def test_add_route_with_view_attr(self): - config = self._makeOne(autocommit=True) - self._registerRenderer(config) - class View(object): - def __init__(self, context, request): - pass - def alt(self): - return 'OK' - config.add_route('name', 'path', view=View, view_attr='alt') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - request = self._makeRequest(config) - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_route_with_view_renderer_alias(self): - config = self._makeOne(autocommit=True) - self._registerRenderer(config) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, - renderer='fixtures/minimal.txt') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.assertEqual(wrapper(None, None).body, 'Hello!') - - def test_add_route_with_view_permission(self): - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy - config = self._makeOne(autocommit=True) - policy = lambda *arg: None - config.registry.registerUtility(policy, IAuthenticationPolicy) - config.registry.registerUtility(policy, IAuthorizationPolicy) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, view_permission='edit') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.assertTrue(hasattr(wrapper, '__call_permissive__')) - - def test_add_route_with_view_permission_alias(self): - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy - config = self._makeOne(autocommit=True) - policy = lambda *arg: None - config.registry.registerUtility(policy, IAuthenticationPolicy) - config.registry.registerUtility(policy, IAuthorizationPolicy) - view = lambda *arg: 'OK' - config.add_route('name', 'path', view=view, permission='edit') - request_type = self._getRouteRequestIface(config, 'name') - wrapper = self._getViewCallable(config, None, request_type) - self._assertRoute(config, 'name', 'path') - self.assertTrue(hasattr(wrapper, '__call_permissive__')) - def test_add_route_no_pattern_with_path(self): config = self._makeOne(autocommit=True) route = config.add_route('name', path='path') @@ -3035,24 +2918,6 @@ class ConfiguratorTests(unittest.TestCase): registeredview = self._getViewCallable(config) self.assertEqual(registeredview.__name__, 'view3') - def test_conflict_route_with_view(self): - from zope.configuration.config import ConfigurationConflictError - config = self._makeOne() - def view1(request): pass - def view2(request): pass - config.add_route('a', '/a', view=view1) - config.add_route('a', '/a', view=view2) - try: - config.commit() - except ConfigurationConflictError, why: - c1, c2, c3, c4 = self._conflictFunctions(why) - self.assertEqual(c1, 'test_conflict_route_with_view') - self.assertEqual(c2, 'test_conflict_route_with_view') - self.assertEqual(c3, 'test_conflict_route_with_view') - self.assertEqual(c4, 'test_conflict_route_with_view') - else: # pragma: no cover - raise AssertionError - def test_conflict_set_notfound_view(self): from zope.configuration.config import ConfigurationConflictError config = self._makeOne() @@ -3106,12 +2971,6 @@ class ConfiguratorTests(unittest.TestCase): self.assertTrue("@view_config(name='two', renderer='string')" in which) - def _conflictFunctions(self, e): - conflicts = e._conflicts.values() - for conflict in conflicts: - for confinst in conflict: - yield confinst[2] - def test___getattr__missing_when_directives_exist(self): config = self._makeOne() directives = {} @@ -3138,6 +2997,220 @@ class ConfiguratorTests(unittest.TestCase): foo_meth = config.foo self.assertTrue(foo_meth.im_func is foo) +class TestConfiguratorDeprecatedFeatures(unittest.TestCase): + def setUp(self): + import warnings + warnings.filterwarnings('ignore') + + def tearDown(self): + import warnings + warnings.resetwarnings() + + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + return Configurator(*arg, **kw) + + def _getRouteRequestIface(self, config, name): + from pyramid.interfaces import IRouteRequest + iface = config.registry.getUtility(IRouteRequest, name) + return iface + + def _getViewCallable(self, config, ctx_iface=None, request_iface=None, + name='', exception_view=False): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + if exception_view: + classifier = IExceptionViewClassifier + else: + classifier = IViewClassifier + if ctx_iface is None: + ctx_iface = Interface + if request_iface is None: + request_iface = IRequest + return config.registry.adapters.lookup( + (classifier, request_iface, ctx_iface), IView, name=name, + default=None) + + def _registerRenderer(self, config, name='.txt'): + from pyramid.interfaces import IRendererFactory + from pyramid.interfaces import ITemplateRenderer + from zope.interface import implements + class Renderer: + implements(ITemplateRenderer) + def __init__(self, info): + self.__class__.info = info + def __call__(self, *arg): + return 'Hello!' + config.registry.registerUtility(Renderer, IRendererFactory, name=name) + return Renderer + + def _assertRoute(self, config, name, path, num_predicates=0): + from pyramid.interfaces import IRoutesMapper + mapper = config.registry.getUtility(IRoutesMapper) + routes = mapper.get_routes() + route = routes[0] + self.assertEqual(len(routes), 1) + self.assertEqual(route.name, name) + self.assertEqual(route.path, path) + self.assertEqual(len(routes[0].predicates), num_predicates) + return route + + def _makeRequest(self, config): + request = DummyRequest() + request.registry = config.registry + return request + + def _conflictFunctions(self, e): + conflicts = e._conflicts.values() + for conflict in conflicts: + for confinst in conflict: + yield confinst[2] + + def test_add_route_with_view(self): + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + + def test_add_route_with_view_context(self): + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_context=IDummy) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, IDummy, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable(config, IOther, request_type) + self.assertEqual(wrapper, None) + + def test_add_route_with_view_exception(self): + from zope.interface import implementedBy + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_context=RuntimeError) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + request_iface=request_type, exception_view=True) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable( + config, ctx_iface=IOther, + request_iface=request_type, exception_view=True) + self.assertEqual(wrapper, None) + + def test_add_route_with_view_for(self): + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_for=IDummy) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, IDummy, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable(config, IOther, request_type) + self.assertEqual(wrapper, None) + + def test_add_route_with_for_(self): + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, for_=IDummy) + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, IDummy, request_type) + self.assertEqual(wrapper(None, None), 'OK') + self._assertRoute(config, 'name', 'path') + wrapper = self._getViewCallable(config, IOther, request_type) + self.assertEqual(wrapper, None) + + def test_add_route_with_view_renderer(self): + config = self._makeOne(autocommit=True) + self._registerRenderer(config) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, + view_renderer='fixtures/minimal.txt') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.assertEqual(wrapper(None, None).body, 'Hello!') + + def test_add_route_with_view_attr(self): + config = self._makeOne(autocommit=True) + self._registerRenderer(config) + class View(object): + def __init__(self, context, request): + pass + def alt(self): + return 'OK' + config.add_route('name', 'path', view=View, view_attr='alt') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + request = self._makeRequest(config) + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_route_with_view_renderer_alias(self): + config = self._makeOne(autocommit=True) + self._registerRenderer(config) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, + renderer='fixtures/minimal.txt') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.assertEqual(wrapper(None, None).body, 'Hello!') + + def test_add_route_with_view_permission(self): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + config = self._makeOne(autocommit=True) + policy = lambda *arg: None + config.registry.registerUtility(policy, IAuthenticationPolicy) + config.registry.registerUtility(policy, IAuthorizationPolicy) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, view_permission='edit') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.assertTrue(hasattr(wrapper, '__call_permissive__')) + + def test_add_route_with_view_permission_alias(self): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + config = self._makeOne(autocommit=True) + policy = lambda *arg: None + config.registry.registerUtility(policy, IAuthenticationPolicy) + config.registry.registerUtility(policy, IAuthorizationPolicy) + view = lambda *arg: 'OK' + config.add_route('name', 'path', view=view, permission='edit') + request_type = self._getRouteRequestIface(config, 'name') + wrapper = self._getViewCallable(config, None, request_type) + self._assertRoute(config, 'name', 'path') + self.assertTrue(hasattr(wrapper, '__call_permissive__')) + + def test_conflict_route_with_view(self): + from zope.configuration.config import ConfigurationConflictError + config = self._makeOne() + def view1(request): pass + def view2(request): pass + config.add_route('a', '/a', view=view1) + config.add_route('a', '/a', view=view2) + try: + config.commit() + except ConfigurationConflictError, why: + c1, c2, c3, c4 = self._conflictFunctions(why) + self.assertEqual(c1, 'test_conflict_route_with_view') + self.assertEqual(c2, 'test_conflict_route_with_view') + self.assertEqual(c3, 'test_conflict_route_with_view') + self.assertEqual(c4, 'test_conflict_route_with_view') + else: # pragma: no cover + raise AssertionError + + class TestConfigurator_add_directive(unittest.TestCase): def setUp(self): |
