diff options
120 files changed, 8917 insertions, 8171 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 6f8ec355b..dfa7bf7fc 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,22 @@ Next release Features -------- +- Add a ``handler`` ZCML directive. This directive does the same thing as + ``pyramid.configuration.add_handler``. + +- A new module named ``pyramid.config`` was added. It subsumes the duties of + the older ``pyramid.configuration`` module. + +- The new ``pyramid.config.Configurator` class has API methods that the older + ``pyramid.configuration.Configurator`` class did not: ``with_context`` (a + classmethod), ``include``, ``action``, and ``commit``. These methods exist + for imperative application extensibility purposes. + +- The ``pyramid.testing.setUp`` function now accepts an ``autocommit`` + keyword argument, which defaults to ``True``. If it is passed ``False``, + the Config object returned by ``setUp`` will be a non-autocommiting Config + object. + - Add logging configuration to all paster templates. - ``pyramid_alchemy``, ``pyramid_routesalchemy``, and ``pylons_sqla`` paster @@ -61,6 +77,26 @@ Documentation - Add a "Modifying Package Structure" section to the project narrative documentation chapter (explain turning a module into a package). +- Documentation was added for the new ``handler`` ZCML directive in the ZCML + section. + +Deprecations +------------ + +- ``pyramid.configuration.Configurator`` is now deprecated. Use + ``pyramid.config.Configurator``, passing its constructor + ``autocommit=True`` instead. The ``pyramid.configuration.Configurator`` + alias will live for a long time, as every application uses it, but its + import now issues a deprecation warning. The + ``pyramid.config.Configurator`` class has the same API as + ``pyramid.configuration.Configurator`` class, which it means to replace, + except by default it is a *non-autocommitting* configurator. The + now-deprecated ``pyramid.configuration.Configurator`` will autocommit every + time a configuration method is called. + + The ``pyramid.configuration`` module remains, but it is deprecated. Use + ``pyramid.config`` instead. + 1.0a4 (2010-11-21) ================== @@ -4,10 +4,14 @@ Pyramid TODOs Must-Have (before 1.0) ---------------------- -- Add a ``handler`` ZCML directive. This implies some slightly dicey - refactoring of the configurator to allow it to generate ZCML - "discriminators" for views and routes, that could be implemented in terms - of "twophase configuration" in "should have" below. +- Narrative docs for ``Configurator.include`` and ``Configurator.commit``. + +- Fix conflict exceptions so they have the right ``info`` when one + configuration method is called from another. + +- Fix session behavior: when a Forbidden/NotFound hander is invoked, we'd + like to allow people to save the session (currently when the response has + an exception attribute, the session is tossed unconditionally). - Provide a .flash API on session object. @@ -31,10 +35,6 @@ Should-Have - Change "Cleaning up After a Request" in the urldispatch chapter to use ``request.add_response_callback``. -- Twophase configuration (config = Configurator(autocommit=False)). Maybe - use ``zope.configuration`` ConfigurationContext as config.registry.ctx and - push execution into the configurator. - Nice-to-Have ------------ diff --git a/docs/api.rst b/docs/api.rst index f8c532cd6..b650c8ded 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -12,7 +12,7 @@ documentation is organized alphabetically by module name. api/authentication api/chameleon_text api/chameleon_zpt - api/configuration + api/config api/events api/exceptions api/httpexceptions diff --git a/docs/api/configuration.rst b/docs/api/config.rst index 6d5c9f16b..64dc4d0dd 100644 --- a/docs/api/configuration.rst +++ b/docs/api/config.rst @@ -1,11 +1,11 @@ .. _configuration_module: -:mod:`pyramid.configuration` -------------------------------- +:mod:`pyramid.config` +--------------------- -.. automodule:: pyramid.configuration +.. automodule:: pyramid.config - .. autoclass:: Configurator(registry=None, package=None, settings=None, root_factory=None, authentication_policy=None, authorization_policy=None, renderers=DEFAULT_RENDERERS, debug_logger=None, locale_negotiator=None, request_factory=None, renderer_globals_factory=None, default_permission=None, session_factory=None) + .. autoclass:: Configurator(registry=None, package=None, settings=None, root_factory=None, authentication_policy=None, authorization_policy=None, renderers=DEFAULT_RENDERERS, debug_logger=None, locale_negotiator=None, request_factory=None, renderer_globals_factory=None, default_permission=None, session_factory=None, autocommit=False) .. attribute:: registry @@ -22,6 +22,12 @@ .. automethod:: get_settings + .. automethod:: commit + + .. automethod:: action + + .. automethod:: include + .. automethod:: with_package .. automethod:: maybe_dotted diff --git a/docs/conventions.rst b/docs/conventions.rst index a17af7230..71c40e104 100644 --- a/docs/conventions.rst +++ b/docs/conventions.rst @@ -22,12 +22,12 @@ concept are presented in the following style: We present Python method names using the following style: - :meth:`pyramid.configuration.Configurator.add_view` + :meth:`pyramid.config.Configurator.add_view` We present Python class names, module names, attributes and global variables using the following style: - :class:`pyramid.configuration.Configurator.registry` + :class:`pyramid.config.Configurator.registry` References to glossary terms are presented using the following style: diff --git a/docs/designdefense.rst b/docs/designdefense.rst index 1409403c4..1a8868883 100644 --- a/docs/designdefense.rst +++ b/docs/designdefense.rst @@ -37,7 +37,7 @@ Too Complex :linenos: from paste.httpserver import serve - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): @@ -545,7 +545,7 @@ everything done completely imperatively. For example, the very most basic from webob import Response from paste.httpserver import serve - from pyramid.configuration import Configurator + from pyramid.config import Configurator def hello_world(request): return Response('Hello world!') @@ -1707,7 +1707,7 @@ where comments take into account what we've discussed in the return Response('Hello world!') if __name__ == '__main__': - from pyramid.configuration import Configurator + from pyramid.config import Configurator config = Configurator() # no global application object. config.add_view(hello_world) # explicit non-decorator registration app = config.make_wsgi_app() # explicitly WSGI diff --git a/docs/glossary.rst b/docs/glossary.rst index ee7a2784d..2494a6cdf 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -585,7 +585,7 @@ Glossary configurator An object used to do :term:`configuration declaration` within an application. The most common configurator is an instance of the - ``pyramid.configuration.Configurator`` class. + ``pyramid.config.Configurator`` class. imperative configuration The configuration mode in which you use Python to call methods on @@ -822,8 +822,8 @@ Glossary View handler A view handler ties together - :meth:`pyramid.configuration.Configurator.add_route` and - :meth:`pyramid.configuration.Configurator.add_view` to make it more + :meth:`pyramid.config.Configurator.add_route` and + :meth:`pyramid.config.Configurator.add_view` to make it more convenient to register a collection of views as a single class when using :term:`url dispatch`. See also :ref:`handlers_chapter`. diff --git a/docs/latexindex.rst b/docs/latexindex.rst index 388297de7..0ef67248b 100644 --- a/docs/latexindex.rst +++ b/docs/latexindex.rst @@ -85,7 +85,7 @@ API Reference api/authentication api/chameleon_text api/chameleon_zpt - api/configuration + api/config api/events api/exceptions api/httpexceptions diff --git a/docs/narr/MyProject/myproject/__init__.py b/docs/narr/MyProject/myproject/__init__.py index 936404c82..05730a115 100644 --- a/docs/narr/MyProject/myproject/__init__.py +++ b/docs/narr/MyProject/myproject/__init__.py @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from myproject.models import get_root def main(global_config, **settings): diff --git a/docs/narr/MyProject/myproject/tests.py b/docs/narr/MyProject/myproject/tests.py index 2f14a946b..b14fb37af 100644 --- a/docs/narr/MyProject/myproject/tests.py +++ b/docs/narr/MyProject/myproject/tests.py @@ -1,11 +1,11 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing class ViewTests(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst index cf1f1ae28..4f26b092a 100644 --- a/docs/narr/configuration.rst +++ b/docs/narr/configuration.rst @@ -38,7 +38,7 @@ imperatively: :linenos: from paste.httpserver import serve - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): @@ -98,7 +98,7 @@ attribute to the ``hello`` function, making it available for a :app:`Pyramid` is willing to :term:`scan` a module or a package and its subpackages for decorations when the -:meth:`pyramid.configuration.Configurator.scan` method is invoked: +:meth:`pyramid.config.Configurator.scan` method is invoked: scanning implies searching for configuration declarations in a package and its subpackages. For example: @@ -116,7 +116,7 @@ and its subpackages. For example: return Response('Hello') if __name__ == '__main__': - from pyramid.configuration import Configurator + from pyramid.config import Configurator config = Configurator() config.scan() app = config.make_wsgi_app() @@ -136,7 +136,7 @@ the intent of the configuration decoration. In the example above, this is best represented as the scanner translating the arguments to :class:`pyramid.view.view_config` into a -call to the :meth:`pyramid.configuration.Configurator.add_view` +call to the :meth:`pyramid.config.Configurator.add_view` method, effectively: .. ignore-next-block diff --git a/docs/narr/declarative.rst b/docs/narr/declarative.rst index eeaed318f..99c5a75ac 100644 --- a/docs/narr/declarative.rst +++ b/docs/narr/declarative.rst @@ -41,7 +41,7 @@ In a file named ``helloworld.py``: from paste.httpserver import serve from pyramid.response import Response - from pyramid.configuration import Configurator + from pyramid.config import Configurator def hello_world(request): return Response('Hello world!') @@ -91,7 +91,7 @@ the ``if __name__ == '__main__'`` section of ``helloworld.py``: In our "declarative" code, we've removed the call to ``add_view`` and replaced it with a call to the -:meth:`pyramid.configuration.Configurator.load_zcml` method so that +:meth:`pyramid.config.Configurator.load_zcml` method so that it now reads as: .. code-block:: python @@ -135,13 +135,13 @@ This ``<view>`` declaration tag performs the same function as the ``add_view`` method that was employed within :ref:`imperative_configuration`. In fact, the ``<view>`` tag is effectively a "macro" which calls the -:meth:`pyramid.configuration.Configurator.add_view` method on your +:meth:`pyramid.config.Configurator.add_view` method on your behalf. The ``<view>`` tag is an example of a :app:`Pyramid` declaration tag. Other such tags include ``<route>`` and ``<scan>``. Each of these tags is effectively a "macro" which calls methods of a -:class:`pyramid.configuration.Configurator` object on your behalf. +:class:`pyramid.config.Configurator` object on your behalf. Essentially, using a :term:`ZCML` file and loading it from the filesystem allows us to put our configuration statements within this @@ -212,7 +212,7 @@ To do so, first, create a file named ``helloworld.py``: .. code-block:: python :linenos: - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid.response import Response from paste.httpserver import serve @@ -277,7 +277,7 @@ within the ``if __name__ == '__main__'`` section of ``helloworld.py``: serve(app, host='0.0.0.0') In our "declarative" code, we've added a call to the -:meth:`pyramid.configuration.Configurator.load_zcml` method with +:meth:`pyramid.config.Configurator.load_zcml` method with the value ``configure.zcml``, and we've removed the lines which read ``config.add_view(hello_world)`` and ``config.add_view(goodbye_world, name='goodbye')``, so that it now reads as: @@ -433,13 +433,13 @@ configurations imperatively, we saw this code: config.add_view(goodbye_world, name='goodbye') Each ``<view>`` declaration tag encountered in a ZCML file effectively -invokes the :meth:`pyramid.configuration.Configurator.add_view` +invokes the :meth:`pyramid.config.Configurator.add_view` method on the behalf of the developer. Various attributes can be specified on the ``<view>`` tag which influence the :term:`view configuration` it creates. Since the relative ordering of calls to -:meth:`pyramid.configuration.Configurator.add_view` doesn't matter +:meth:`pyramid.config.Configurator.add_view` doesn't matter (see the sidebar entitled *View Dispatch and Ordering* within :ref:`adding_configuration`), the relative order of ``<view>`` tags in ZCML doesn't matter either. The following ZCML orderings are @@ -501,7 +501,7 @@ file points to is scanned. return Response('Hello') if __name__ == '__main__': - from pyramid.configuration import Configurator + from pyramid.config import Configurator config = Configurator() config.begin() config.load_zcml('configure.zcml') @@ -644,12 +644,11 @@ See :ref:`view_directive` for complete ZCML directive documentation. Configuring a Route via ZCML ---------------------------- -Instead of using the imperative -:meth:`pyramid.configuration.Configurator.add_route` method to add a new -route, you can alternately use :term:`ZCML`. :ref:`route_directive` -statements in a :term:`ZCML` file used by your application is a sign that -you're using :term:`URL dispatch`. For example, the following :term:`ZCML -declaration` causes a route to be added to the application. +Instead of using the imperative :meth:`pyramid.config.Configurator.add_route` +method to add a new route, you can alternately use :term:`ZCML`. +:ref:`route_directive` statements in a :term:`ZCML` file. For example, the +following :term:`ZCML declaration` causes a route to be added to the +application. .. code-block:: xml :linenos: @@ -679,6 +678,46 @@ is the order that they appear relative to each other in the ZCML file. See :ref:`route_directive` for full ``route`` ZCML directive documentation. +.. _zcml_handler_configuration: + +Configuring a Handler via ZCML +------------------------------ + +Instead of using the imperative +:meth:`pyramid.config.Configurator.add_handler` method to add a new +route, you can alternately use :term:`ZCML`. :ref:`handler_directive` +statements in a :term:`ZCML` file used by your application is a sign that +you're using :term:`URL dispatch`. For example, the following :term:`ZCML +declaration` causes a route to be added to the application. + +.. code-block:: xml + :linenos: + + <handler + route_name="myroute" + pattern="/prefix/{action}" + handler=".handlers.MyHandler" + /> + +.. note:: + + Values prefixed with a period (``.``) within the values of ZCML attributes + such as the ``handler`` attribute of a ``handler`` directive mean + "relative to the Python package directory in which this :term:`ZCML` file + is stored". So if the above ``handler`` declaration was made inside a + ``configure.zcml`` file that lived in the ``hello`` package, you could + replace the relative ``.views.MyHandler`` with the absolute + ``hello.views.MyHandler`` Either the relative or absolute form is + functionally equivalent. It's often useful to use the relative form, in + case your package's name changes. It's also shorter to type. + +The order that the routes attached to handlers are evaluated when declarative +configuration is used is the order that they appear relative to each other in +the ZCML file. + +See :ref:`handler_directive` for full ``handler`` ZCML directive +documentation. + .. index:: triple: view; zcml; static resource @@ -778,7 +817,7 @@ listening on ``example.com`` must be itself configured to respond properly to such a request. The :func:`pyramid.url.static_url` API is discussed in more detail later in this chapter. -The :meth:`pyramid.configuration.Configurator.add_static_view` method offers +The :meth:`pyramid.config.Configurator.add_static_view` method offers an imperative equivalent to the ``static`` ZCML directive. Use of the ``add_static_view`` imperative configuration method is completely equivalent to using ZCML for the same purpose. See :ref:`static_resources_section` for @@ -1028,7 +1067,7 @@ with ``.jinja2`` as its ``renderer`` value. The ``name`` passed to the ``renderer=`` to the view configuration. See also :ref:`renderer_directive` and -:meth:`pyramid.configuration.Configurator.add_renderer`. +:meth:`pyramid.config.Configurator.add_renderer`. Overriding an Existing Renderer @@ -1097,7 +1136,7 @@ tag): /> See also :ref:`renderer_directive` and -:meth:`pyramid.configuration.Configurator.add_renderer`. +:meth:`pyramid.config.Configurator.add_renderer`. .. _zcml_adding_a_translation_directory: diff --git a/docs/narr/events.rst b/docs/narr/events.rst index 1edc2c5b0..06b30883f 100644 --- a/docs/narr/events.rst +++ b/docs/narr/events.rst @@ -34,7 +34,7 @@ when it's called. The mere existence of a subscriber function, however, is not sufficient to arrange for it to be called. To arrange for the subscriber to be called, you'll need to use the -:meth:`pyramid.configuration.Configurator.add_subscriber` method or you'll +:meth:`pyramid.config.Configurator.add_subscriber` method or you'll need to use the :func:`pyramid.events.subscriber` decorator to decorate a function found via a :term:`scan`. @@ -42,7 +42,7 @@ function found via a :term:`scan`. You can imperatively configure a subscriber function to be called for some event type via the - :meth:`pyramid.configuration.Configurator.add_subscriber` + :meth:`pyramid.config.Configurator.add_subscriber` method (see also :term:`Configurator`): .. code-block:: python @@ -53,12 +53,12 @@ function found via a :term:`scan`. from subscribers import mysubscriber # "config" below is assumed to be an instance of a - # pyramid.configuration.Configurator object + # pyramid.config.Configurator object config.add_subscriber(mysubscriber, NewRequest) The first argument to - :meth:`pyramid.configuration.Configurator.add_subscriber` is the + :meth:`pyramid.config.Configurator.add_subscriber` is the subscriber function (or a :term:`dotted Python name` which refers to a subscriber callable); the second argument is the event type. @@ -127,7 +127,7 @@ configuration startup: .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_subscriber('myproject.subscribers.handle_new_request', 'pyramid.events.NewRequest') diff --git a/docs/narr/extending.rst b/docs/narr/extending.rst index 902fb001d..1f9fd1288 100644 --- a/docs/narr/extending.rst +++ b/docs/narr/extending.rst @@ -23,7 +23,7 @@ relying on :term:`configuration decoration` meant to be detected via a :term:`scan`, and you mustn't configure your :app:`Pyramid` application *imperatively* by using any code which configures the application through methods of the :term:`Configurator` (except for -the :meth:`pyramid.configuration.Configurator.load_zcml` method). +the :meth:`pyramid.config.Configurator.load_zcml` method). Instead, you must always use :term:`ZCML` for the equivalent purposes. :term:`ZCML` declarations that belong to an application can be @@ -104,7 +104,7 @@ configuration imperatively, one of two things may be true: :class:`pyramid.view.view_config` decorators, you can just prevent a :term:`scan` from happening (by omitting the ``<scan>`` declaration from ZCML or omitting any call to the - :meth:`pyramid.configuration.Configurator.scan` method). This + :meth:`pyramid.config.Configurator.scan` method). This will cause the decorators to do nothing. At this point, you will need to convert all the configuration done in decorators into equivalent :term:`ZCML` and add that ZCML to a separate Python @@ -170,7 +170,7 @@ something like this: - In the ``__init__.py`` of the new package, load the ``configure.zcml`` file of the new package using the - :meth:`pyramid.configuration.Configurator.load_zcml` method. + :meth:`pyramid.config.Configurator.load_zcml` method. .. index:: pair: overriding; views diff --git a/docs/narr/firstapp.rst b/docs/narr/firstapp.rst index 86152c8b2..b40faee3d 100644 --- a/docs/narr/firstapp.rst +++ b/docs/narr/firstapp.rst @@ -26,7 +26,7 @@ configured imperatively: .. code-block:: python :linenos: - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid.response import Response from paste.httpserver import serve @@ -68,12 +68,12 @@ The above script defines the following set of imports: .. code-block:: python :linenos: - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid.response import Response from paste.httpserver import serve The script imports the ``Configurator`` class from the -``pyramid.configuration`` module. This class is used to configure +``pyramid.config`` module. This class is used to configure :app:`Pyramid` for a particular application. An instance of this class provides methods which help configure various parts of :app:`Pyramid` for a given application deployment. @@ -180,7 +180,7 @@ code within the ``if`` block should only be run during a direct script execution. The ``config = Configurator()`` line above creates an instance of the -:class:`pyramid.configuration.Configurator` class. The resulting +:class:`pyramid.config.Configurator` class. The resulting ``config`` object represents an API which the script uses to configure this particular :app:`Pyramid` application. Methods called on the Configurator will cause registrations to be made in a @@ -199,7 +199,7 @@ Adding Configuration config.add_view(goodbye_world, name='goodbye') Each of these lines calls the -:meth:`pyramid.configuration.Configurator.add_view` method. The +:meth:`pyramid.config.Configurator.add_view` method. The ``add_view`` method of a configurator registers a :term:`view configuration` within the :term:`application registry`. A :term:`view configuration` represents a set of circumstances related to the @@ -249,7 +249,7 @@ set of predicates) is always invoked. In this application, :app:`Pyramid` chooses the most specific view callable based only on view :term:`predicate` applicability. The ordering of calls to -:meth:`pyramid.configuration.Configurator.add_view` is never very +:meth:`pyramid.config.Configurator.add_view` is never very important. We can register ``goodbye_world`` first and ``hello_world`` second; :app:`Pyramid` will still give us the most specific callable when a request is dispatched to it. @@ -269,7 +269,7 @@ WSGI Application Creation After configuring views and ending configuration, the script creates a WSGI *application* via the -:meth:`pyramid.configuration.Configurator.make_wsgi_app` method. A +:meth:`pyramid.config.Configurator.make_wsgi_app` method. A call to ``make_wsgi_app`` implies that all configuration is finished (meaning all method calls to the configurator which set up views, and various other configuration settings have been performed). The @@ -332,7 +332,7 @@ References ---------- For more information about the API of a :term:`Configurator` object, -see :class:`pyramid.configuration.Configurator` . +see :class:`pyramid.config.Configurator` . For more information about :term:`view configuration`, see :ref:`views_chapter`. diff --git a/docs/narr/handlers.rst b/docs/narr/handlers.rst index d28c9d120..f6e658cf0 100644 --- a/docs/narr/handlers.rst +++ b/docs/narr/handlers.rst @@ -6,8 +6,8 @@ View Handlers Along with normal view callables, :app:`Pyramid` provides the concept of a :term:`view handler`. Using a view handler instead of a plain :term:`view callable` makes it unnecessary to call -:meth:`pyramid.configuration.Configurator.add_route` (and/or -:meth:`pyramid.configuration.Configurator.add_view`) "by hand" multiple +:meth:`pyramid.config.Configurator.add_route` (and/or +:meth:`pyramid.config.Configurator.add_view`) "by hand" multiple times, making it more pleasant to register a collection of views as a single class when using :term:`url dispatch`. The view handler machinery also introduces the concept of an ``action``, which is used as a :term:`view @@ -27,7 +27,7 @@ during configuration. After the view handler class is instantiated, a method on the instance is called. The method which is called depends on the view handler configuration. -The :meth:`pyramid.configuration.Configurator.add_handler` method will +The :meth:`pyramid.config.Configurator.add_handler` method will scan the handler class and automatically set up views for methods that are auto-exposed, or were decorated with the :class:`~pyramid.view.action` decorator. This decorator is used to setup @@ -56,7 +56,7 @@ Here's an example view handler class: return {} An accompanying call to the -:meth:`~pyramid.configuration.Configurator.add_handler` for the handler must +:meth:`~pyramid.config.Configurator.add_handler` for the handler must be performed in order to register it with the system: .. code-block:: python @@ -87,15 +87,22 @@ This will result one of the methods that are configured for the ``action`` of of the method is the same as the action name: 'index'. However, this need not be the case, as we will see below. -Using :meth:`~pyramid.configuration.Configurator.add_handler` +.. note:: + + Handler configuration may also be added to the system via :term:`ZCML` (see + :ref:`zcml_handler_configuration`). + +.. _using_add_handler: + +Using :meth:`~pyramid.config.Configurator.add_handler` ------------------------------------------------------------- -When calling :meth:`~pyramid.configuration.Configurator.add_handler`, an +When calling :meth:`~pyramid.config.Configurator.add_handler`, an ``action`` is required in either the route pattern or as a keyword argument, but **cannot appear in both places**. A ``handler`` argument must also be supplied, which can be either a :term:`resource specification` or a Python reference to the handler class. Additional keyword arguments are passed -directly through to :meth:`pyramid.configuration.Configurator.add_route`. +directly through to :meth:`pyramid.config.Configurator.add_route`. For example: @@ -106,10 +113,10 @@ For example: handler='mypackage.handlers:MyHandler') In larger applications, it is advised to use a :term:`resource specification` -with :meth:`~pyramid.configuration.Configurator.add_handler` to avoid having +with :meth:`~pyramid.config.Configurator.add_handler` to avoid having to import every handler class. -Multiple :meth:`~pyramid.configuration.Configurator.add_handler` calls can +Multiple :meth:`~pyramid.config.Configurator.add_handler` calls can specify the same handler, to register specific route names for different handler/action combinations. For example: @@ -130,7 +137,7 @@ The handler class specified can have a single class level attribute called ``None``. It's used to determine which method names will result in additional view configurations being registered. -When :meth:`~pyramid.configuration.Configurator.add_handler` runs, every +When :meth:`~pyramid.config.Configurator.add_handler` runs, every method in the handler class will be searched and a view registered if the method name matches the ``__autoexpose__`` regular expression, or if the method was decorated with :class:`~pyramid.view.action`. @@ -170,12 +177,12 @@ Action Decorator The :class:`~pyramid.view.action` decorator registers view configuration information on the handler method, which is used by -:meth:`~pyramid.configuration.Configurator.add_handler` to setup the view +:meth:`~pyramid.config.Configurator.add_handler` to setup the view configuration. All keyword arguments are recorded, and passed to -:meth:`~pyramid.configuration.Configurator.add_view`. Any valid keyword -arguments for :meth:`~pyramid.configuration.Configurator.add_view` can thus be +:meth:`~pyramid.config.Configurator.add_view`. Any valid keyword +arguments for :meth:`~pyramid.config.Configurator.add_view` can thus be used with the :class:`~pyramid.view.action` decorator to further restrict when the view will be called. diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index ada96f897..10f463a17 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -29,7 +29,7 @@ class as the ``context`` of the view configuration. If your application uses :term:`imperative configuration`, you can replace the Not Found view by using the - :meth:`pyramid.configuration.Configurator.add_view` method to + :meth:`pyramid.config.Configurator.add_view` method to register an "exception view": .. code-block:: python @@ -118,7 +118,7 @@ class as the ``context`` of the view configuration. If your application uses :term:`imperative configuration`, you can replace the Forbidden view by using the - :meth:`pyramid.configuration.Configurator.add_view` method to + :meth:`pyramid.config.Configurator.add_view` method to register an "exception view": .. code-block:: python @@ -368,13 +368,13 @@ In the below, we assume it lives in a package named Lastly, if you're doing imperative configuration, and you'd rather do it after you've already constructed a :term:`configurator` it can also be registered via the -:meth:`pyramid.configuration.Configurator.set_request_factory` +:meth:`pyramid.config.Configurator.set_request_factory` method: .. code-block:: python :linenos: - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid.request import Request class MyRequest(Request): @@ -435,13 +435,13 @@ named ``mypackage.mymodule``. Lastly, if you're doing imperative configuration, and you'd rather do it after you've already constructed a :term:`configurator` it can also be registered via the -:meth:`pyramid.configuration.Configurator.set_renderer_globals_factory` +:meth:`pyramid.config.Configurator.set_renderer_globals_factory` method: .. code-block:: python :linenos: - from pyramid.configuration import Configurator + from pyramid.config import Configurator def renderer_globals_factory(system): return {'a':1} @@ -474,7 +474,7 @@ that can be used for this purpose. For example: An object of this type is sent as an event just before a :term:`renderer` is invoked (but *after* the application-level renderer globals factory added via -:class:`pyramid.configuration.Configurator.set_renderer_globals_factory`, if +:class:`pyramid.config.Configurator.set_renderer_globals_factory`, if any, has injected its own keys into the renderer globals dictionary). If a subscriber attempts to add a key that already exist in the renderer @@ -659,7 +659,7 @@ performed, enabling you to set up the utility in advance: :linenos: from paste.httpserver import serve - from pyramid.configuration import Configurator + from pyramid.config import Configurator class UtilityImplementation: diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst index 4acedc124..da77a28f0 100644 --- a/docs/narr/hybrid.rst +++ b/docs/narr/hybrid.rst @@ -40,7 +40,7 @@ configuration: .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_route('foobar', '{foo}/{bar}', view='myproject.views.foobar') config.add_route('bazbuz', '{baz}/{buz}', view='myproject.views.bazbuz') @@ -50,7 +50,7 @@ and when that route is matched during a request, the view callable named by the ``view`` attribute is invoked. Typically, an application that uses only URL dispatch won't perform any calls -to :meth:`pyramid.configuration.Configurator.add_view` in its startup code. +to :meth:`pyramid.config.Configurator.add_view` in its startup code. Traversal Only ~~~~~~~~~~~~~~ @@ -61,7 +61,7 @@ declarations that look like this: .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_view('mypackage.views.foobar', name='foobar') config.add_view('mypackage.views.bazbuz', name='bazbuz') @@ -72,7 +72,7 @@ When the above configuration is applied to an application, the be called when the URL ``/bazbuz`` is visited. Typically, an application that uses traversal exclusively won't perform any -calls to :meth:`pyramid.configuration.Configurator.add_route` in its startup +calls to :meth:`pyramid.config.Configurator.add_route` in its startup code. Hybrid Applications @@ -158,11 +158,11 @@ match is straightforward. When a route is matched: argument, the *global* :term:`root factory` will be called to generate a :term:`root` object. The global root factory is the callable implied by the ``root_factory`` argument passed to - :class:`pyramid.configuration.Configurator` at application + :class:`pyramid.config.Configurator` at application startup time. - If a ``root_factory`` argument is not provided to the - :class:`pyramid.configuration.Configurator` at startup time, a + :class:`pyramid.config.Configurator` at startup time, a *default* root factory is used. The default root factory is used to generate a root object. @@ -263,7 +263,7 @@ thing to do. We could have also used our ``root_factory`` callable as the ``root_factory`` argument of the - :class:`pyramid.configuration.Configurator` constructor, instead + :class:`pyramid.config.Configurator` constructor, instead of associating it with a particular route inside the route's configuration. Every hybrid route configuration that is matched but which does *not* name a ``factory`` attribute will use the use @@ -305,13 +305,13 @@ invoked after a route matches: config.add_view('mypackage.views.myview', route_name='home') Note that the above call to -:meth:`pyramid.configuration.Configurator.add_view` includes a ``route_name`` +:meth:`pyramid.config.Configurator.add_view` includes a ``route_name`` argument. View configurations that include a ``route_name`` argument are meant to associate a particular view declaration with a route, using the route's name, in order to indicate that the view should *only be invoked when the route matches*. -Calls to :meth:`pyramid.configuration.Configurator.add_view` may pass a +Calls to :meth:`pyramid.config.Configurator.add_view` may pass a ``route_name`` attribute, which refers to the value of an existing route's ``name`` argument. In the above example, the route name is ``home``, referring to the name of the route defined above it. @@ -362,7 +362,7 @@ Using the ``traverse`` Argument In a Route Definition Rather than using the ``*traverse`` remainder marker in a pattern, you can use the ``traverse`` argument to the -:meth:`pyramid.configuration.Configurator.add_route` method. +:meth:`pyramid.config.Configurator.add_route` method. When you use the ``*traverse`` remainder marker, the traversal path is limited to being the remainder segments of a request URL when a route @@ -370,7 +370,7 @@ matches. However, when you use the ``traverse`` argument or attribute, you have more control over how to compose a traversal path. Here's a use of the ``traverse`` pattern in a call to -:meth:`pyramid.configuration.Configurator.add_route`: +:meth:`pyramid.config.Configurator.add_route`: .. code-block:: python :linenos: @@ -477,7 +477,7 @@ startup time. config.add_view('myproject.views.another', route_name='home') This is because the ``view`` argument to the -:meth:`pyramid.configuration.Configurator.add_route` above is an *implicit* +:meth:`pyramid.config.Configurator.add_route` above is an *implicit* default view when that route matches. ``add_route`` calls don't *need* to supply a view attribute. For example, this ``add_route`` call: diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst index 603b08cef..d8cc5cb1c 100644 --- a/docs/narr/i18n.rst +++ b/docs/narr/i18n.rst @@ -192,7 +192,7 @@ factory is best practice. Using your own unique translation domain allows another person to reuse your application without needing to merge your translation files with his own. Instead, he can just include your package's :term:`translation directory` via the -:meth:`pyramid.configuration.Configurator.add_translation_dirs` +:meth:`pyramid.config.Configurator.add_translation_dirs` method. .. note:: @@ -747,13 +747,13 @@ Localization-Related Deployment Settings A :app:`Pyramid` application will have a ``default_locale_name`` setting. This value represents the :term:`default locale name` used when the :term:`locale negotiator` returns ``None``. Pass it to the -:mod:`pyramid.configuration.Configurator` constructor at startup +:mod:`pyramid.config.Configurator` constructor at startup time: .. code-block:: python :linenos: - from pyramid.configuration import Configurator + from pyramid.config import Configurator config = Configurator(settings={'default_locale_name':'de'}) You may alternately supply a ``default_locale_name`` via an @@ -874,13 +874,13 @@ directory in the translation directory) within your :app:`Pyramid` application to be available to use for translation services. You can add a translation directory imperatively by using the -:meth:`pyramid.configuration.Configurator.add_translation_dirs` during +:meth:`pyramid.config.Configurator.add_translation_dirs` during application startup. For example: .. code-block:: python :linenos: - from pyramid.configuration import Configurator + from pyramid.config import Configurator config.begin() config.add_translation_dirs('my.application:locale/', 'another.application:locale/') @@ -888,7 +888,7 @@ application startup. For example: config.end() A message catalog in a translation directory added via -:meth:`pyramid.configuration.Configurator.add_translation_dirs` +:meth:`pyramid.config.Configurator.add_translation_dirs` will be merged into translations from a message catalog added earlier if both translation directories contain translations for the same locale and :term:`translation domain`. @@ -1000,17 +1000,17 @@ You may add your newly created locale negotiator to your application's configuration by passing an object which can act as the negotiator (or a :term:`dotted Python name` referring to the object) as the ``locale_negotiator`` argument of the -:class:`pyramid.configuration.Configurator` instance during application +:class:`pyramid.config.Configurator` instance during application startup. For example: .. code-block:: python :linenos: - from pyramid.configuration import Configurator + from pyramid.config import Configurator config = Configurator(locale_negotiator=my_locale_negotiator) Alternately, use the -:meth:`pyramid.configuration.Configurator.set_locale_negotiator` +:meth:`pyramid.config.Configurator.set_locale_negotiator` method. For example: @@ -1018,7 +1018,7 @@ For example: .. code-block:: python :linenos: - from pyramid.configuration import Configurator + from pyramid.config import Configurator config = Configurator() config.begin() config.set_locale_negotiator(my_locale_negotiator) diff --git a/docs/narr/models.rst b/docs/narr/models.rst index 20f443571..2c72e766d 100644 --- a/docs/narr/models.rst +++ b/docs/narr/models.rst @@ -9,14 +9,14 @@ omnipresent in :app:`Pyramid`: willing to walk over when :term:`traversal` is used. - The ``context`` and ``containment`` arguments to - :meth:`pyramid.configuration.Configurator.add_view` often + :meth:`pyramid.config.Configurator.add_view` often reference a model class. - A :term:`root factory` returns a model instance. - A model instance is generated as a result of :term:`url dispatch` (see the ``factory`` argument to - :meth:`pyramid.configuration.Configurator.add_route`). + :meth:`pyramid.config.Configurator.add_route`). - A model instance is exposed to :term:`view` code as the :term:`context` of a view. diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 6036feb90..f8a9017db 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -754,7 +754,7 @@ also informs Python that the directory which contains it is a *package*. :linenos: #. Line 1 imports the :term:`Configurator` class from - :mod:`pyramid.configuration` that we use later. + :mod:`pyramid.config` that we use later. #. Line 2 imports the ``get_root`` function from :mod:`myproject.models` that we use later. @@ -915,7 +915,7 @@ made for you by any paster template, you can decide to lay your code out any way you see fit. For example, the configuration method named -:meth:`~pyramid.configuration.Configurator.add_view` requires you to pass a +:meth:`~pyramid.config.Configurator.add_view` requires you to pass a :term:`dotted Python name` or a direct object reference as the class or function to be used as a view. By default, the ``pyramid_starter`` paster template would have you add view functions to the ``views.py`` module in your diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst index 3ff8af90b..c87157472 100644 --- a/docs/narr/resources.rst +++ b/docs/narr/resources.rst @@ -114,40 +114,6 @@ following kinds of resources defined in any Python package: - Any other resource (or set of resources) addressed by code that uses the setuptools :term:`pkg_resources` API. -Usually, overriding a resource in an existing application means -performing the following steps: - -- Create a new Python package. The easiest way to do this is to - create a new :app:`Pyramid` application using the "paster" - template mechanism. See :ref:`creating_a_project` for more - information. - -- Install the new package into the same Python environment as the - original application (e.g. ``python setup.py develop`` or ``python - setup.py install``). - -- Change the ``configure.zcml`` in the new package to include one or - more ``resource`` ZCML directives (see :ref:`resource_directive` - below). The new package's ``configure.zcml`` should then include - the original :app:`Pyramid` application's ``configure.zcml`` via - an include statement, e.g. ``<include - package="theoriginalpackage"/>``. - -- Add override resources to the package as necessary. - -- Change the Paste ``.ini`` file that starts up the original - application. Add a ``configure_zcml`` statement within the - application's section in the file which points at your *new* - package's ``configure.zcml`` file. See :ref:`environment_chapter` - for more information about this setting. - -Note that overriding resources is not the only way to extend or modify -the behavior of an existing :app:`Pyramid` application. A "heavier -hammer" way to do the same thing is explained in -:ref:`extending_chapter`. The heavier hammer way allows you to -replace a :term:`view` wholesale rather than resources that might be -used by a view. - .. index:: single: override_resource @@ -157,7 +123,7 @@ The ``override_resource`` API ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An individual call to -:meth:`pyramid.configuration.Configurator.override_resource` can +:meth:`pyramid.config.Configurator.override_resource` can override a single resource. For example: .. ignore-next-block @@ -213,7 +179,7 @@ will need to register two overrides. The package name in a specification may start with a dot, meaning that the package is relative to the package in which the configuration construction file resides (or the ``package`` argument to the -:class:`pyramid.configuration.Configurator` class construction). +:class:`pyramid.config.Configurator` class construction). For example: .. ignore-next-block @@ -245,7 +211,7 @@ The ``resource`` ZCML Directive ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Instead of using -:meth:`pyramid.configuration.Configurator.override_resource` during +:meth:`pyramid.config.Configurator.override_resource` during :term:`imperative configuration`, an equivalent can be used to perform all the tasks described above within :term:`ZCML`. The ZCML ``resource`` tag is a frontend to using ``override_resource``. diff --git a/docs/narr/security.rst b/docs/narr/security.rst index 2c696772a..5edbc3ec3 100644 --- a/docs/narr/security.rst +++ b/docs/narr/security.rst @@ -63,14 +63,14 @@ Enabling an Authorization Policy Imperatively ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Passing an ``authorization_policy`` argument to the constructor of the -:class:`pyramid.configuration.Configurator` class enables an +:class:`pyramid.config.Configurator` class enables an authorization policy. You must also enable an :term:`authentication policy` in order to enable the authorization policy. This is because authorization, in general, depends upon authentication. Use the ``authentication_policy`` argument to the -:class:`pyramid.configuration.Configurator` class during +:class:`pyramid.config.Configurator` class during application setup to specify an authentication policy. For example: @@ -79,7 +79,7 @@ For example: .. code-block:: python :linenos: - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy authentication_policy = AuthTktAuthenticationPolicy('seekrit') @@ -127,12 +127,12 @@ name permissions whatever you like. For example, the following view declaration protects the view named ``add_entry.html`` when invoked against a ``Blog`` context with the ``add`` -permission using the :meth:`pyramid.configuration.Configurator.add_view` API: +permission using the :meth:`pyramid.config.Configurator.add_view` API: .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_view('mypackage.views.blog_entry_add_view', name='add_entry.html', @@ -183,10 +183,10 @@ These APIs are in support of configuring a default permission for an application: - The ``default_permission`` constructor argument to the - :mod:`pyramid.configuration.Configurator` constructor. + :mod:`pyramid.config.Configurator` constructor. - The - :meth:`pyramid.configuration.Configurator.set_default_permission` + :meth:`pyramid.config.Configurator.set_default_permission` method. - The :ref:`default_permission_directive` ZCML directive. @@ -582,7 +582,7 @@ that implements the following interface: current user on subsequent requests. """ After you do so, you can pass an instance of such a class into the -:class:`pyramid.configuration.Configurator` class at configuration +:class:`pyramid.config.Configurator` class at configuration time as ``authentication_policy`` to use it. .. index:: @@ -626,5 +626,5 @@ following interface: permission """ After you do so, you can pass an instance of such a class into the -:class:`pyramid.configuration.Configurator` class at configuration +:class:`pyramid.config.Configurator` class at configuration time as ``authorization_policy`` to use it. diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst index eea035975..8547a92cc 100644 --- a/docs/narr/sessions.rst +++ b/docs/narr/sessions.rst @@ -35,7 +35,7 @@ tampered with. You can configure this session factory in your :app:`Pyramid` application by using the ``session_factory`` argument to the -:class:`pyramid.configuration.Configurator` class: +:class:`pyramid.config.Configurator` class: .. code-block:: python :linenos: @@ -43,7 +43,7 @@ application by using the ``session_factory`` argument to the from pyramid.session import UnencryptedCookieSessionFactoryConfig my_session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet') - from pyramid.configuration import Configurator + from pyramid.config import Configurator config = Configurator(session_factory = my_session_factory) .. warning:: diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst index 9103bf20f..2199f2e59 100644 --- a/docs/narr/startup.rst +++ b/docs/narr/startup.rst @@ -95,7 +95,7 @@ press ``return`` after running ``paster serve development.ini``. 'debug_templates':'true', 'default_locale_name':'en'}``. #. The ``app`` function first constructs a - :class:`pyramid.configuration.Configurator` instance, passing + :class:`pyramid.config.Configurator` instance, passing ``get_root`` to it as its ``root_factory`` argument, and ``settings`` dictionary captured via the ``**settings`` kwarg as its ``settings`` argument. @@ -112,12 +112,12 @@ press ``return`` after running ``paster serve development.ini``. 'default_locale_name':'en'}``. #. The ``app`` function then calls various methods on the an instance of the - class :class:`pyramid.configuration.Configurator` method. The intent of + class :class:`pyramid.config.Configurator` method. The intent of calling these methods is to populate an :term:`application registry`, which represents the :app:`Pyramid` configuration related to the application. -#. The :meth:`pyramid.configuration.Configurator.make_wsgi_app` method is +#. The :meth:`pyramid.config.Configurator.make_wsgi_app` method is called. The result is a :term:`router` instance. The router is associated with the :term:`application registry` implied by the configurator previously populated by other methods run against the @@ -146,7 +146,7 @@ Deployment Settings ------------------- Note that an augmented version of the values passed as ``**settings`` to the -:class:`pyramid.configuration.Configurator` constructor will be available in +:class:`pyramid.config.Configurator` constructor will be available in :app:`Pyramid` :term:`view callable` code as ``request.registry.settings``. You can create objects you wish to access later from view code, and put them into the dictionary you pass to the configurator as ``settings``. They will diff --git a/docs/narr/static.rst b/docs/narr/static.rst index bed922953..172fb08d4 100644 --- a/docs/narr/static.rst +++ b/docs/narr/static.rst @@ -13,17 +13,17 @@ how to configure :app:`Pyramid` to do so. Serving Static Resources ------------------------ -Use the :meth:`pyramid.configuration.Configurator.add_static_view` to +Use the :meth:`pyramid.config.Configurator.add_static_view` to instruct :app:`Pyramid` to serve static resources such as JavaScript and CSS files. This mechanism makes static files available at a name relative to the application root URL, e.g. ``/static``. Note that the ``path`` provided to -:meth:`pyramid.configuration.Configurator.add_static_view` may be a fully +:meth:`pyramid.config.Configurator.add_static_view` may be a fully qualified :term:`resource specification`, or an *absolute path*. Here's an example of a use of -:meth:`pyramid.configuration.Configurator.add_static_view` that will serve +:meth:`pyramid.config.Configurator.add_static_view` that will serve files up under the ``/static`` URL from the ``/var/www/static`` directory of the computer which runs the :app:`Pyramid` application using an absolute path. @@ -31,11 +31,11 @@ path. .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_static_view(name='static', path='/var/www/static') Here's an example of -:meth:`pyramid.configuration.Configurator.add_static_view` that will serve +:meth:`pyramid.config.Configurator.add_static_view` that will serve files up under the ``/static`` URL from the ``a/b/c/static`` directory of the Python package named ``some_package`` using a fully qualified :term:`resource specification`. @@ -43,50 +43,70 @@ specification`. .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_static_view(name='static', path='some_package:a/b/c/static') -Whether you use a fully qualified resource specification, or an absolute -path for ``path`` in the directive, it specifies a filesystem directory -where static files will be served from. Once you place files there, you -will then be able to view them from a browser at URLs prefixed with the -directive's ``name``. For instance, if the ``static`` directive's -``name`` is ``static``, and the static directive's ``path`` is -``/path/to/static/dir``, ``http://localhost:6543/static/foo.js`` will -serve the file ``/path/to/static/dir/foo.js``. The static directory may -contain subdirectories recursively, and any subdirectories may hold -files; these will be resolved by the static view as you would expect. - -The ``name`` argument of the call to -:meth:`pyramid.configuration.Configurator.add_static_view` can be either -a *view name*, or a *URL*. In the above examples, the ``name`` argument -is a view name. When ``name`` is a *URL* (or any string with a slash -(``/``) in it), static resources can be served from an external -webserver. In this mode, the ``name`` is used as the URL prefix when -generating a URL using the :func:`pyramid.url.static_url` function. - -For example, :meth:`pyramid.configuration.Configurator.add_static_view` may +Whether you use for ``path`` a fully qualified resource specification, or an +absolute path, when you place your static files on the filesystem in the +directory represented as the ``path`` of the directive, you will then be able +to view the static files in this directory via a browser at URLs prefixed +with the directive's ``name``. For instance if the ``static`` directive's +``name`` is ``static`` and the static directive's ``path`` is +``/path/to/static``, ``http://localhost:6543/static/foo.js`` will return the +file ``/path/to/static/dir/foo.js``. The static directory may contain +subdirectories recursively, and any subdirectories may hold files; these will +be resolved by the static view as you would expect. + +While the ``path`` argument can be a number of different things, the ``name`` +argument of the call to +:meth:`pyramid.config.Configurator.add_static_view` can also be one of +a number of things: a *view name* or a *URL*. The above examples have shown +usage of the ``name`` argument as a view name. When ``name`` is a *URL* (or +any string with a slash (``/``) in it), static resources can be served from +an external webserver. In this mode, the ``name`` is used as the URL prefix +when generating a URL using :func:`pyramid.url.static_url`. + +.. note:: + + Using :func:`pyramid.url.static_url` in conjunction with a + :meth:`pyramid.config.Configurator.add_static_view` makes it + possible to put static media on a separate webserver during production (if + the ``name`` argument to + :meth:`pyramid.config.Configurator.add_static_view` is a URL), + while keeping static media package-internal and served by the development + webserver during development (if the ``name`` argument to + :meth:`pyramid.config.Configurator.add_static_view` is a view + name). To create such a circumstance, we suggest using the + :attr:`pyramid.registry.Registry.settings` API in conjunction with a + setting in the application ``.ini`` file named ``media_location``. Then + set the value of ``media_location`` to either a view name or a URL + depending on whether the application is being run in development or in + production (use a different `.ini`` file for production than you do for + development). This is just a suggestion for a pattern; any setting name + other than ``media_location`` could be used. + +For example, :meth:`pyramid.config.Configurator.add_static_view` may be fed a ``name`` argument which is ``http://example.com/images``: .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_static_view(name='http://example.com/images', path='mypackage:images') -Because :meth:`pyramid.configuration.Configurator.add_static_view` is +Because :meth:`pyramid.config.Configurator.add_static_view` is provided with a ``name`` argument that is the URL prefix ``http://example.com/images``, subsequent calls to :func:`pyramid.url.static_url` with paths that start with the ``path`` -argument passed to :meth:`pyramid.configuration.Configurator.add_static_view` +argument passed to :meth:`pyramid.config.Configurator.add_static_view` will generate a URL something like ``http://example.com/images/logo.png``. The external webserver listening on ``example.com`` must be itself configured to respond properly to such a request. The :func:`pyramid.url.static_url` API is discussed in more detail later in this chapter. The :ref:`static_directive` ZCML directive offers an declarative equivalent -to :meth:`pyramid.configuration.Configurator.add_static_view`. Use of the +to :meth:`pyramid.config.Configurator.add_static_view`. Use of the :ref:`static_directive` ZCML directive is completely equivalent to using imperative configuration for the same purpose. @@ -118,7 +138,7 @@ imperative configuration for the same purpose. Generating Static Resource URLs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When a :meth:`pyramid.configuration.Configurator.add_static_view` method is +When a :meth:`pyramid.config.Configurator.add_static_view` method is used to register a static resource directory, a special helper API named :func:`pyramid.url.static_url` can be used to generate the appropriate URL for a package resource that lives in one of the directories named by the static @@ -168,7 +188,7 @@ properly after the rename. URLs may also be generated by :func:`pyramid.url.static_url` to static resources that live *outside* the :app:`Pyramid` application. This will -happen when the :meth:`pyramid.configuration.Configurator.add_static_view` +happen when the :meth:`pyramid.config.Configurator.add_static_view` API associated with the path fed to :func:`pyramid.url.static_url` is a *URL* instead of a view name. For example, the ``name`` argument may be ``http://example.com`` while the the ``path`` given may be @@ -224,7 +244,7 @@ your application root as below. (e.g. ``anotherpackage:some/subdirectory``). Subsequently, you may wire this view up to be accessible as ``/static`` using -the :mod:`pyramid.configuration.Configurator.add_view` method in your +the :mod:`pyramid.config.Configurator.add_view` method in your application's startup code against either the class or interface that represents your root object. diff --git a/docs/narr/threadlocals.rst b/docs/narr/threadlocals.rst index 9b04d5914..f540fa255 100644 --- a/docs/narr/threadlocals.rst +++ b/docs/narr/threadlocals.rst @@ -75,8 +75,8 @@ defined entirely by the behavior of a pyramid :term:`Router`. However, during unit testing, no Router code is ever invoked, and the definition of "current" is defined by the boundary between calls to -the :meth:`pyramid.configuration.Configurator.begin` and -:meth:`pyramid.configuration.Configurator.end` methods (or between +the :meth:`pyramid.config.Configurator.begin` and +:meth:`pyramid.config.Configurator.end` methods (or between calls to the :func:`pyramid.testing.setUp` and :func:`pyramid.testing.tearDown` functions). These functions push and pop the threadlocal stack when the system is under test. See @@ -156,7 +156,7 @@ place to use the ``get_current_request``. Use of the :func:`pyramid.threadlocal.get_current_registry` function should be limited to testing scenarios. The registry made current by use of the -:meth:`pyramid.configuration.Configurator.begin` method during a +:meth:`pyramid.config.Configurator.begin` method during a test (or via :func:`pyramid.testing.setUp`) when you do not pass one in is available to you via this API. diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index 2a10a5022..01729c4bd 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -180,7 +180,7 @@ named ``config``: config = Configurator(root_factory=Root) Using the ``root_factory`` argument to a -:class:`pyramid.configuration.Configurator` constructor tells your +:class:`pyramid.config.Configurator` constructor tells your :app:`Pyramid` application to call this root factory to generate a root object whenever a request enters the application. This root factory is also known as the global root factory. A root factory can diff --git a/docs/narr/unittesting.rst b/docs/narr/unittesting.rst index 26035726b..3c31f69b6 100644 --- a/docs/narr/unittesting.rst +++ b/docs/narr/unittesting.rst @@ -79,9 +79,18 @@ If your code uses these ``get_current_*`` functions or calls :app:`Pyramid` code which uses ``get_current_*`` functions, you will need to construct a :term:`Configurator` and call its ``begin`` method within the ``setUp`` method of your unit test and call the same -configurator's ``end`` method within the ``tearDown`` method of your +Configurator's ``end`` method within the ``tearDown`` method of your unit test. +We'll also instruct the Configurator we use during testing to *autocommit*. +Normally when a Configurator is used by an application, it defers performing +any "real work" until its ``.commit`` method is called (often implicitly by +the :meth:`pyramid.config.Configurator.make_wsgi_app` method). Passing +``autocommit=True`` to the Configurator constructor causes the Configurator +to perform all actions implied by methods called on it immediately, which is +more convenient for unit-testing purposes than needing to call +:meth:`pyramid.config.Configurator.commit` in each test. + The use of a Configurator and its ``begin`` and ``end`` methods allows you to supply each unit test method in a test case with an environment that has an isolated registry and an isolated request for the duration @@ -91,11 +100,11 @@ of a single test. Here's an example of using this feature: :linenos: import unittest - from pyramid.configuration import Configurator + from pyramid.config import Configurator class MyTest(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): @@ -103,13 +112,12 @@ of a single test. Here's an example of using this feature: The above will make sure that :func:`pyramid.threadlocal.get_current_registry` will return the -:term:`application registry` associated with the ``config`` -Configurator instance when -:func:`pyramid.threadlocal.get_current_registry` is called in a -test case method attached to ``MyTest``. Each test case method -attached to ``MyTest`` will use an isolated registry. +:term:`application registry` associated with the ``config`` Configurator +instance when :func:`pyramid.threadlocal.get_current_registry` is called in a +test case method attached to ``MyTest``. Each test case method attached to +``MyTest`` will use an isolated registry. -The :meth:`pyramid.configuration.Configurator.begin` method accepts +The :meth:`pyramid.config.Configurator.begin` method accepts various arguments that influence the code run during the test. See the :ref:`configuration_module` chapter for information about the API of a :term:`Configurator`, including its ``begin`` and ``end`` @@ -118,19 +126,19 @@ methods. If you also want to make :func:`pyramid.get_current_request` return something other than ``None`` during the course of a single test, you can pass a :term:`request` object into the -:meth:`pyramid.configuration.Configurator.begin` method of the +:meth:`pyramid.config.Configurator.begin` method of the Configurator within the ``setUp`` method of your test: .. code-block:: python :linenos: import unittest - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid import testing class MyTest(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) request = testing.DummyRequest() self.config.begin(request=request) @@ -150,17 +158,16 @@ construct than a "real" :app:`Pyramid` request object. What? ~~~~~ -Thread local data structures are always a bit confusing, especially -when they're used by frameworks. Sorry. So here's a rule of thumb: -if you don't *know* whether you're calling code that uses the +Thread local data structures are always a bit confusing, especially when +they're used by frameworks. Sorry. So here's a rule of thumb: if you don't +*know* whether you're calling code that uses the :func:`pyramid.threadlocal.get_current_registry` or -:func:`pyramid.threadlocal.get_current_request` functions, or you -don't care about any of this, but you still want to write test code, -just always create a Configurator instance and call its ``begin`` -method within the ``setUp`` of a unit test, then subsequently call its -``end`` method in the test's ``tearDown``. This won't really hurt -anything if the application you're testing does not call any -``get_current*`` function. +:func:`pyramid.threadlocal.get_current_request` functions, or you don't care +about any of this, but you still want to write test code, just always create +an autocommitting Configurator instance and call its ``begin`` method within +the ``setUp`` of a unit test, then subsequently call its ``end`` method in +the test's ``tearDown``. This won't really hurt anything if the application +you're testing does not call any ``get_current*`` function. .. index:: single: pyramid.testing @@ -196,7 +203,7 @@ a :term:`application registry` using :term:`configuration declaration` calls made against a :term:`Configurator` (sometimes deferring to the application's ``configure.zcml`` :term:`ZCML` file via ``load_zcml``). But if this application registry is not created and populated -(e.g. with an :meth:`pyramid.configuration.Configurator.add_view` +(e.g. with an :meth:`pyramid.config.Configurator.add_view` :term:`configuration declaration` or ``view`` declarations in :term:`ZCML`), like when you invoke application code via a unit test, :app:`Pyramid` API functions will tend to fail. @@ -213,12 +220,12 @@ used the testing API. :linenos: import unittest - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid import testing class MyTest(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): @@ -249,7 +256,7 @@ The first test method, ``test_view_fn_not_submitted`` tests the ``view_fn`` function in the case that no "form" values (represented by request.params) have been submitted. Its first line registers a "dummy template renderer" named ``templates/show.pt`` via the -:meth:`pyramid.configuration.Configurator.testing_add_renderer` +:meth:`pyramid.config.Configurator.testing_add_renderer` method; this method returns a :class:`pyramid.testing.DummyTemplateRenderer` instance which we hang on to for later. @@ -281,10 +288,10 @@ attribute is ``Yo``, as this is what is expected of the view function in the branch it's testing. Note that the test calls the -:meth:`pyramid.configuration.Configurator.begin` method in its +:meth:`pyramid.config.Configurator.begin` method in its ``setUp`` method and the ``end`` method of the same in its ``tearDown`` method. If you use any of the -:class:`pyramid.configuration.Configurator` APIs during testing, be +:class:`pyramid.config.Configurator` APIs during testing, be sure to use this pattern in your test case's ``setUp`` and ``tearDown``; these methods make sure you're using a "fresh" :term:`application registry` per test run. @@ -335,7 +342,7 @@ environment. import unittest - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid import testing class ViewIntegrationTests(unittest.TestCase): @@ -345,7 +352,7 @@ environment. (including dependent registrations for pyramid itself). """ import myapp - self.config = Configurator(package=myapp) + self.config = Configurator(package=myapp, autocommit=True) self.config.begin() self.config.load_zcml('myapp:configure.zcml') @@ -367,7 +374,7 @@ environment. str(len(body)))) Unless you cannot avoid it, you should prefer writing unit tests that -use the :class:`pyramid.configuration.Configurator` API to set up +use the :class:`pyramid.config.Configurator` API to set up the right "mock" registrations rather than creating an integration test. Unit tests will run faster (because they do less for each test) and the result of a unit test is usually easier to make assertions diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index 209c4f4d2..c84a5ca06 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -35,7 +35,7 @@ a more natural fit for creating an application that manipulates "flat" data. The presence of calls to the -:meth:`pyramid.configuration.Configurator.add_route` method in imperative +:meth:`pyramid.config.Configurator.add_route` method in imperative configuration within your application is a sign that you're using :term:`URL dispatch`. @@ -81,7 +81,7 @@ predicate` parameters, and a set of :term:`view` parameters. Configuring a Route via The ``add_route`` Configurator Method ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The :meth:`pyramid.configuration.Configurator.add_route` method +The :meth:`pyramid.config.Configurator.add_route` method adds a single :term:`route configuration` to the :term:`application registry`. Here's an example: @@ -89,7 +89,7 @@ registry`. Here's an example: .. code-block:: python # "config" below is presumed to be an instance of the - # pyramid.configuration.Configurator class; "myview" is assumed + # pyramid.config.Configurator class; "myview" is assumed # to be a "view callable" function from views import myview config.add_route('myroute', '/prefix/{one}/{two}', view=myview) @@ -118,7 +118,7 @@ Here's an example route configuration that references a view callable: :linenos: # "config" below is presumed to be an instance of the - # pyramid.configuration.Configurator class; "myview" is assumed + # pyramid.config.Configurator class; "myview" is assumed # to be a "view callable" function from myproject.views import myview config.add_route('myroute', '/prefix/{one}/{two}', view=myview) @@ -130,7 +130,7 @@ rather than an actual callable: :linenos: # "config" below is presumed to be an instance of the - # pyramid.configuration.Configurator class; "myview" is assumed + # pyramid.config.Configurator class; "myview" is assumed # to be a "view callable" function from myproject.views import myview config.add_route('myroute', '/prefix/{one}/{two}', @@ -377,7 +377,7 @@ they are added to the application at startup time. This is unlike :term:`traversal`, which depends on emergent behavior which happens as a result of traversing a graph. -For routes added via the :mod:`pyramid.configuration.Configurator.add_route` +For routes added via the :mod:`pyramid.config.Configurator.add_route` method, the order that routes are evaluated is the order in which they are added to the configuration imperatively. @@ -657,7 +657,7 @@ Custom Route Predicates ~~~~~~~~~~~~~~~~~~~~~~~ Each of the predicate callables fed to the ``custom_predicates`` argument of -:meth:`pyramid.configuration.Configurator.add_route` must be a callable +:meth:`pyramid.config.Configurator.add_route` must be a callable accepting two arguments. The first argument passed to a custom predicate is a dictionary conventionally named ``info``. The second argument is the current :term:`request` object. @@ -692,7 +692,7 @@ generate a predicate function named ``num_one_two_or_three``, which ensures that the ``num`` segment is one of the values ``one``, ``two``, or ``three`` , and use the result as a custom predicate by feeding it inside a tuple to the ``custom_predicates`` argument to -:meth:`pyramid.configuration.Configurator.add_route`. +:meth:`pyramid.config.Configurator.add_route`. A custom route predicate may also *modify* the ``match`` dictionary. For instance, a predicate might do some type conversion of values: @@ -968,7 +968,7 @@ factory` configured at startup time (the ``root_factory`` argument to the :term:`Configurator` used to configure the application). You can override this behavior by passing in a ``factory`` argument to the -:meth:`pyramid.configuration.Configurator.add_route` method for a particular +:meth:`pyramid.config.Configurator.add_route` method for a particular route. The ``factory`` should be a callable that accepts a :term:`request` and returns an instance of a class that will be the context used by the view. @@ -1033,7 +1033,7 @@ Matching the Root URL It's not entirely obvious how to use a route pattern to match the root URL ("/"). To do so, give the empty string as a pattern in a call to -:meth:`pyramid.configuration.Configurator.add_route`: +:meth:`pyramid.config.Configurator.add_route`: .. code-block:: python :linenos: diff --git a/docs/narr/views.rst b/docs/narr/views.rst index d30fcdae5..8a689be21 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -338,7 +338,7 @@ within :ref:`the_response`. View configuration can vary the renderer associated with a view callable via the ``renderer`` attribute. For example, this call to -:meth:`pyramid.configuration.Configurator.add_view` associates the ``json`` +:meth:`pyramid.config.Configurator.add_view` associates the ``json`` renderer with a view callable: .. code-block:: python @@ -469,7 +469,7 @@ contain values serializable by :func:`json.dumps`. You can configure a view to use the JSON renderer by naming ``json`` as the ``renderer`` argument of a view configuration, e.g. by using -:meth:`pyramid.configuration.Configurator.add_view`: +:meth:`pyramid.config.Configurator.add_view`: .. code-block:: python :linenos: @@ -537,7 +537,7 @@ renderer: .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_view('myproject.views.hello_world', name='hello', @@ -588,7 +588,7 @@ Here's an example view configuration which uses a relative path: .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_view('myproject.views.hello_world', name='hello', @@ -689,7 +689,7 @@ override an existing :term:`renderer factory`, and which add a new renderer factory. Renderers can be registered imperatively using the -:meth:`pyramid.configuration.Configurator.add_renderer` API. +:meth:`pyramid.config.Configurator.add_renderer` API. .. note:: The tasks described in this section can also be performed via :term:`declarative configuration`. See @@ -768,12 +768,12 @@ There are essentially two different kinds of renderer factories: ``my.package`` Python :term:`package`. Here's an example of the registration of a simple renderer factory via -:meth:`pyramid.configuration.Configurator.add_renderer`: +:meth:`pyramid.config.Configurator.add_renderer`: .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_renderer(name='amf', factory='my.package.MyAMFRenderer') @@ -836,7 +836,7 @@ to the ``Jinja2Renderer`` constructor will be the full value that was set as ``renderer=`` in the view configuration. See also :ref:`renderer_directive` and -:meth:`pyramid.configuration.Configurator.add_renderer`. +:meth:`pyramid.config.Configurator.add_renderer`. Overriding an Existing Renderer +++++++++++++++++++++++++++++++ @@ -845,7 +845,7 @@ You can associate more than one filename extension with the same existing renderer implementation as necessary if you need to use a different file extension for the same kinds of templates. For example, to associate the ``.zpt`` extension with the Chameleon ZPT renderer factory, use the -:meth:`pyramid.configuration.Configurator.add_renderer` method: +:meth:`pyramid.config.Configurator.add_renderer` method: .. code-block:: python :linenos: @@ -1145,8 +1145,8 @@ View configuration is performed in one of these ways: object as per :class:`pyramid.view.view_config` and :ref:`mapping_views_using_a_decorator_section`. -- by using the :meth:`pyramid.configuration.Configurator.add_view` - method as per :meth:`pyramid.configuration.Configurator.add_view` +- by using the :meth:`pyramid.config.Configurator.add_view` + method as per :meth:`pyramid.config.Configurator.add_view` and :ref:`mapping_views_using_imperative_config_section`. Both of these mechanisms is completely equivalent to the other. @@ -1157,7 +1157,7 @@ Both of these mechanisms is completely equivalent to the other. A view configuration might also be performed by virtue of :term:`route configuration`. View configuration via route configuration is performed by -using the :meth:`pyramid.configuration.Configurator.add_route` method to +using the :meth:`pyramid.config.Configurator.add_route` method to create a route with a ``view`` argument. .. note:: ZCML users can use :ref:`route_directive` to perform the same task. @@ -1458,7 +1458,7 @@ configuration`, like ZCML, but in decorator form. :class:`pyramid.view.view_config` can be used to associate :term:`view configuration` information -- as done via the equivalent imperative code or ZCML -- with a function that acts as a :app:`Pyramid` view callable. All -arguments to the :meth:`pyramid.configuration.Configurator.add_view` method +arguments to the :meth:`pyramid.config.Configurator.add_view` method (save for the ``view`` argument) are available in decorator form and mean precisely the same thing. @@ -1512,13 +1512,13 @@ perform view configuration. All that the decorator does is "annotate" the function with your configuration declarations, it doesn't process them. To make :app:`Pyramid` process your :class:`pyramid.view.view_config` declarations, you *must* do use the -``scan`` method of a :class:`pyramid.configuration.Configurator`: +``scan`` method of a :class:`pyramid.config.Configurator`: .. code-block:: python :linenos: # config is assumed to be an instance of the - # pyramid.configuration.Configurator class + # pyramid.config.Configurator class config.scan() .. note:: See :ref:`zcml_scanning` for information about how to invoke a scan @@ -1529,7 +1529,7 @@ about what happens when code is scanned for configuration declarations resulting from use of decorators like :class:`pyramid.view.view_config`. See :ref:`configuration_module` for additional API arguments to the -:meth:`pyramid.configuration.Configurator.scan` method. For example, the +:meth:`pyramid.config.Configurator.scan` method. For example, the method allows you to supply a ``package`` argument to better control exactly *which* code will be scanned. @@ -1661,7 +1661,7 @@ equivalently as the below: View Configuration Using the ``add_view`` Method of a Configurator ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The :meth:`pyramid.configuration.Configurator.add_view` method +The :meth:`pyramid.config.Configurator.add_view` method within :ref:`configuration_module` is used to configure a view imperatively. The arguments to this method are very similar to the arguments that you provide to the ``@view_config`` decorator. For @@ -1676,13 +1676,13 @@ example: return Response('hello!') # config is assumed to be an instance of the - # pyramid.configuration.Configurator class + # pyramid.config.Configurator class config.add_view(hello_world, name='hello.html') The first argument, ``view``, is required. It must either be a Python object which is the view itself or a :term:`dotted Python name` to such an object. All other arguments are optional. See -:meth:`pyramid.configuration.Configurator.add_view` for more +:meth:`pyramid.config.Configurator.add_view` for more information. .. index:: @@ -1758,7 +1758,7 @@ this interface. .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_view('mypackage.views.hello_world', name='hello.html', context='mypackage.models.IHello') @@ -1787,6 +1787,7 @@ view configuration, see :ref:`models_which_implement_interfaces`. Configuring View Security ~~~~~~~~~~~~~~~~~~~~~~~~~ +<<<<<<< HEAD If an :term:`authorization policy` is active, any :term:`permission` attached to a :term:`view configuration` found during view lookup will be verified. This will ensure that the currently authenticated user @@ -1794,11 +1795,19 @@ possesses that permission against the :term:`context` before the view function is actually called. Here's an example of specifying a permission in a view configuration using :meth:`pyramid.configuration.Configurator.add_view`: +======= +If a :term:`authorization policy` is active, any :term:`permission` attached +to a :term:`view configuration` found during view lookup will be consulted to +ensure that the currently authenticated user possesses that permission +against the :term:`context` before the view function is actually called. +Here's an example of specifying a permission in a view configuration using +:meth:`pyramid.config.Configurator.add_view`: +>>>>>>> fee38663daccc0130d0c34dbc5a14e67bef2e183 .. code-block:: python :linenos: - # config is an instance of pyramid.configuration.Configurator + # config is an instance of pyramid.config.Configurator config.add_view('myproject.views.add_entry', name='add.html', context='myproject.models.IBlog', permission='add') diff --git a/docs/narr/zca.rst b/docs/narr/zca.rst index 7c7617a3d..c930ecc50 100644 --- a/docs/narr/zca.rst +++ b/docs/narr/zca.rst @@ -93,7 +93,7 @@ component registry associated with your :app:`Pyramid` application. There are three ways to fix this: by disusing the ZCA global API entirely, by using -:meth:`pyramid.configuration.Configurator.hook_zca` or by passing +:meth:`pyramid.config.Configurator.hook_zca` or by passing the ZCA global registry to the :term:`Configurator` constructor at startup time. We'll describe all three methods in this section. @@ -154,7 +154,7 @@ Consider the following bit of idiomatic :app:`Pyramid` startup code: :linenos: from zope.component import getGlobalSiteManager - from pyramid.configuration import Configurator + from pyramid.config import Configurator def app(global_settings, **settings): config = Configurator(settings=settings) @@ -186,14 +186,14 @@ always return the global ZCA registry (the one in To "fix" this and make the ZCA global APIs use the "current" BFG registry, you need to call -:meth:`pyramid.configuration.Configurator.hook_zca` within your +:meth:`pyramid.config.Configurator.hook_zca` within your setup code. For example: .. code-block:: python :linenos: from zope.component import getGlobalSiteManager - from pyramid.configuration import Configurator + from pyramid.config import Configurator def app(global_settings, **settings): config = Configurator(settings=settings) @@ -243,7 +243,7 @@ registry at startup time instead of constructing a new one: :linenos: from zope.component import getGlobalSiteManager - from pyramid.configuration import Configurator + from pyramid.config import Configurator def app(global_settings, **settings): globalreg = getGlobalSiteManager() @@ -282,7 +282,7 @@ they should actually be calling ``zope.component.getSiteManager``. ``zope.component.getSiteManager`` can be overridden by :app:`Pyramid` via -:meth:`pyramid.configuration.Configurator.hook_zca`, while +:meth:`pyramid.config.Configurator.hook_zca`, while ``zope.component.getGlobalSiteManager`` cannot. Directives that use ``zope.component.getGlobalSiteManager`` are effectively broken; no ZCML directive should be using this function to find a registry to diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst index a94a1632d..3dbf10bd8 100644 --- a/docs/tutorials/wiki/basiclayout.rst +++ b/docs/tutorials/wiki/basiclayout.rst @@ -50,10 +50,10 @@ entry point happens to be the ``app`` function within the file named #. *Line 16*. Load the ``configure.zcml`` file from our package using the - :meth:`pyramid.configuration.Configurator.load_zcml` method. + :meth:`pyramid.config.Configurator.load_zcml` method. #. *Line 17*. Use the - :meth:`pyramid.configuration.Configurator.make_wsgi_app` method + :meth:`pyramid.config.Configurator.make_wsgi_app` method to return a :term:`WSGI` application. Configuration With ``configure.zcml`` diff --git a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py index a8a3c513e..742627a3f 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from repoze.zodbconn.finder import PersistentApplicationFinder from tutorial.models import appmaker diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py index 45e4d722b..8325865c0 100644 --- a/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from repoze.zodbconn.finder import PersistentApplicationFinder from tutorial.models import appmaker diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/tests.py b/docs/tutorials/wiki/src/basiclayout/tutorial/tests.py index e11b3625e..a43b917a8 100644 --- a/docs/tutorials/wiki/src/basiclayout/tutorial/tests.py +++ b/docs/tutorials/wiki/src/basiclayout/tutorial/tests.py @@ -1,11 +1,11 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing class ViewTests(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/docs/tutorials/wiki/src/models/tutorial/__init__.py b/docs/tutorials/wiki/src/models/tutorial/__init__.py index 66e2f05ee..cf0d14b2d 100644 --- a/docs/tutorials/wiki/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/models/tutorial/__init__.py @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from repoze.zodbconn.finder import PersistentApplicationFinder from tutorial.models import appmaker diff --git a/docs/tutorials/wiki/src/models/tutorial/tests.py b/docs/tutorials/wiki/src/models/tutorial/tests.py index 2f5bc48e3..25cb04cc6 100644 --- a/docs/tutorials/wiki/src/models/tutorial/tests.py +++ b/docs/tutorials/wiki/src/models/tutorial/tests.py @@ -1,6 +1,6 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing class PageModelTests(unittest.TestCase): @@ -50,7 +50,7 @@ class AppmakerTests(unittest.TestCase): class ViewTests(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py b/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py index 66e2f05ee..cf0d14b2d 100644 --- a/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/viewdecorators/tutorial/__init__.py @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from repoze.zodbconn.finder import PersistentApplicationFinder from tutorial.models import appmaker diff --git a/docs/tutorials/wiki/src/views/tutorial/__init__.py b/docs/tutorials/wiki/src/views/tutorial/__init__.py index 66e2f05ee..cf0d14b2d 100644 --- a/docs/tutorials/wiki/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki/src/views/tutorial/__init__.py @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from repoze.zodbconn.finder import PersistentApplicationFinder from tutorial.models import appmaker diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index b7e20a95b..8d30ab807 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -68,7 +68,7 @@ represents. .. note: Although we don't use the functionality here, the ``factory`` used to create route contexts may differ per-route as opposed to globally. See the ``factory`` argument to - :meth:`pyramid.configuration.Configurator.add_route` for more info. + :meth:`pyramid.config.Configurator.add_route` for more info. We'll pass the ``RootFactory`` we created in the step above in as the ``root_factory`` argument to a :term:`Configurator`. @@ -85,7 +85,7 @@ For any :app:`Pyramid` application to perform authorization, we need to add a We'll change our ``__init__.py`` file to enable an ``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to enable declarative security checking. We'll also change ``__init__.py`` to add a -:meth:`pyramid.configuration.Configurator.add_view` call to points at our +:meth:`pyramid.config.Configurator.add_view` call to points at our ``login`` :term:`view callable`, also known as a :term:`forbidden view`. This configures our newly created login view to show up when :app:`Pyramid` detects that a view invocation can not be authorized. Also, we'll add diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst index a9f7b2282..565bd0e96 100644 --- a/docs/tutorials/wiki2/basiclayout.rst +++ b/docs/tutorials/wiki2/basiclayout.rst @@ -47,7 +47,7 @@ function: ``db_string``, etc. #. *Line 12*. We call - :meth:`pyramid.configuration.Configurator.add_static_view` with the + :meth:`pyramid.config.Configurator.add_static_view` with the arguments ``static`` (the name), and ``tutorial:static`` (the path). This registers a static resource view which will match any URL that starts with ``/static/``. This will serve up static resources for us from within the @@ -59,13 +59,13 @@ function: such as a CSS file. #. *Lines 13-14*. Register a :term:`route configuration` via the - :meth:`pyramid.configuration.Configurator.add_route` method that will be + :meth:`pyramid.config.Configurator.add_route` method that will be used when the URL is ``/``. Since this route has an ``pattern`` equalling ``/`` it is the "default" route. The argument named ``view`` with the value ``tutorial.views.my_view`` is the dotted name to a *function* we write (generated by the ``pyramid_routesalchemy`` template) that is given a ``request`` object and which returns a response or a dictionary. You - will use :meth:`pyramid.configuration.Configurator.add_route` statements + will use :meth:`pyramid.config.Configurator.add_route` statements in a :term:`URL dispatch` based application to map URLs to code. This route also names a ``view_renderer``, which is a template which lives in the ``templates`` subdirectory of the package. When the @@ -73,7 +73,7 @@ function: will use this template to create a response. #. *Line 15*. We use the - :meth:`pyramid.configuration.Configurator.make_wsgi_app` method to return + :meth:`pyramid.config.Configurator.make_wsgi_app` method to return a :term:`WSGI` application. Content Models with ``models.py`` diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst index 0f446bb4e..e3d611136 100644 --- a/docs/tutorials/wiki2/definingviews.rst +++ b/docs/tutorials/wiki2/definingviews.rst @@ -23,7 +23,7 @@ assumed to return a :term:`response` object. The request passed to every view that is called as the result of a route match has an attribute named ``matchdict`` that contains the elements placed into the URL by the ``pattern`` of a ``route`` statement. For instance, if a -call to :meth:`pyramid.configuration.Configurator.add_route` in +call to :meth:`pyramid.config.Configurator.add_route` in ``__init__.py`` had the pattern ``{one}/{two}``, and the URL at ``http://example.com/foo/bar`` was invoked, matching this pattern, the matchdict dictionary attached to the request passed to the view would have a @@ -255,7 +255,7 @@ body of this guide, however it is available `online This CSS file will be accessed via e.g. ``http://localhost:6543/static/style.css`` by virtue of the call we've -made to :meth:`pyramid.configuration.Configurator.add_static_view` within our +made to :meth:`pyramid.config.Configurator.add_static_view` within our ``__init__.py`` file. Any number and type of static resources can be placed in this directory (or subdirectories) and are just referred to by URL within templates. @@ -264,7 +264,7 @@ Mapping Views to URLs in ``__init__.py`` ======================================== The ``__init__.py`` file contains -:meth:`pyramid.configuration.Configurator.add_route` calls which serve to map +:meth:`pyramid.config.Configurator.add_route` calls which serve to map URLs via :term:`url dispatch` to view functions. First, we’ll get rid of the existing route created by the template using the name ``home``. It’s only an example and isn’t relevant to our application. diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index dbac349b9..025b94927 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py index c78899797..1020a8b99 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py @@ -1,6 +1,6 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing def _initTestingDB(): @@ -20,7 +20,7 @@ def _registerRoutes(config): class ViewWikiTests(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): @@ -36,7 +36,7 @@ class ViewWikiTests(unittest.TestCase): class ViewPageTests(unittest.TestCase): def setUp(self): self.session = _initTestingDB() - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): @@ -71,7 +71,7 @@ class ViewPageTests(unittest.TestCase): class AddPageTests(unittest.TestCase): def setUp(self): self.session = _initTestingDB() - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): @@ -104,7 +104,7 @@ class AddPageTests(unittest.TestCase): class EditPageTests(unittest.TestCase): def setUp(self): self.session = _initTestingDB() - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py index 5236a538d..d27b891c0 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from sqlalchemy import engine_from_config from tutorial.models import initialize_sql diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py index 2db1bc5b6..fa3788340 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py @@ -1,5 +1,5 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing def _initTestingDB(): @@ -10,7 +10,7 @@ def _initTestingDB(): class TestMyView(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() _initTestingDB() diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py index e1baa2d64..c912a015b 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from sqlalchemy import engine_from_config from tutorial.models import initialize_sql diff --git a/docs/tutorials/wiki2/src/models/tutorial/tests.py b/docs/tutorials/wiki2/src/models/tutorial/tests.py index 72f0c89d8..42b0aaada 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/models/tutorial/tests.py @@ -1,5 +1,5 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing def _initTestingDB(): @@ -9,7 +9,7 @@ def _initTestingDB(): class TestMyView(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() _initTestingDB() diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py index 91c299e24..334fde814 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from sqlalchemy import engine_from_config from tutorial.models import initialize_sql diff --git a/docs/tutorials/wiki2/src/views/tutorial/tests.py b/docs/tutorials/wiki2/src/views/tutorial/tests.py index 435e4b588..7b770f927 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/views/tutorial/tests.py @@ -1,6 +1,6 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing def _initTestingDB(): @@ -20,7 +20,7 @@ def _registerRoutes(config): class ViewWikiTests(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): @@ -36,7 +36,7 @@ class ViewWikiTests(unittest.TestCase): class ViewPageTests(unittest.TestCase): def setUp(self): self.session = _initTestingDB() - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): @@ -71,7 +71,7 @@ class ViewPageTests(unittest.TestCase): class AddPageTests(unittest.TestCase): def setUp(self): self.session = _initTestingDB() - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): @@ -104,7 +104,7 @@ class AddPageTests(unittest.TestCase): class EditPageTests(unittest.TestCase): def setUp(self): self.session = _initTestingDB() - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/docs/tutorials/zeo/index.rst b/docs/tutorials/zeo/index.rst index 229ec2ffc..b47c6c477 100644 --- a/docs/tutorials/zeo/index.rst +++ b/docs/tutorials/zeo/index.rst @@ -164,7 +164,7 @@ Configuration .. code-block:: python - from pyramid.configuration import Configurator + from pyramid.config import Configurator from repoze.zodbconn.finder import PersistentApplicationFinder from myapp.models import appmaker import transaction diff --git a/docs/zcml.rst b/docs/zcml.rst index 60bbbf574..5913f9460 100644 --- a/docs/zcml.rst +++ b/docs/zcml.rst @@ -16,6 +16,7 @@ documentation is organized alphabetically by directive name. zcml/configure zcml/default_permission zcml/forbidden + zcml/handler zcml/include zcml/localenegotiator zcml/notfound diff --git a/docs/zcml/aclauthorizationpolicy.rst b/docs/zcml/aclauthorizationpolicy.rst index 7eecf6c6e..2c66da0c8 100644 --- a/docs/zcml/aclauthorizationpolicy.rst +++ b/docs/zcml/aclauthorizationpolicy.rst @@ -24,7 +24,7 @@ Alternatives You may create an instance of the :class:`pyramid.authorization.ACLAuthorizationPolicy` and pass it -to the :class:`pyramid.configuration.Configurator` constructor as +to the :class:`pyramid.config.Configurator` constructor as the ``authorization_policy`` argument during initial application configuration. diff --git a/docs/zcml/authtktauthenticationpolicy.rst b/docs/zcml/authtktauthenticationpolicy.rst index 1119de9b0..25be4186c 100644 --- a/docs/zcml/authtktauthenticationpolicy.rst +++ b/docs/zcml/authtktauthenticationpolicy.rst @@ -91,7 +91,7 @@ Alternatives You may create an instance of the :class:`pyramid.authentication.AuthTktAuthenticationPolicy` and -pass it to the :class:`pyramid.configuration.Configurator` +pass it to the :class:`pyramid.config.Configurator` constructor as the ``authentication_policy`` argument during initial application configuration. diff --git a/docs/zcml/default_permission.rst b/docs/zcml/default_permission.rst index beb60db1a..54e720ea6 100644 --- a/docs/zcml/default_permission.rst +++ b/docs/zcml/default_permission.rst @@ -43,11 +43,11 @@ Alternatives ~~~~~~~~~~~~ Using the ``default_permission`` argument to the -:class:`pyramid.configuration.Configurator` constructor can be used +:class:`pyramid.config.Configurator` constructor can be used to achieve the same purpose. Using the -:meth:`pyramid.configuration.Configurator.set_default_permission` +:meth:`pyramid.config.Configurator.set_default_permission` method can be used to achieve the same purpose when using imperative configuration. diff --git a/docs/zcml/forbidden.rst b/docs/zcml/forbidden.rst index f5b1b4644..7ea6b85fd 100644 --- a/docs/zcml/forbidden.rst +++ b/docs/zcml/forbidden.rst @@ -68,7 +68,7 @@ Alternatives Use the :ref:`view_directive` directive with a ``context`` that names the :exc:`pyramid.exceptions.Forbidden` class. -Use the :meth:`pyramid.configuration.Configurator.add_view` method, +Use the :meth:`pyramid.config.Configurator.add_view` method, passing it a ``context`` which is the :exc:`pyramid.exceptions.Forbidden` class. diff --git a/docs/zcml/handler.rst b/docs/zcml/handler.rst new file mode 100644 index 000000000..301bf7895 --- /dev/null +++ b/docs/zcml/handler.rst @@ -0,0 +1,158 @@ +.. _handler_directive: + +``handler`` +----------- + +The ``handler`` directive adds the configuration of a :term:`view handler` to +the :term:`application registry`. + +Attributes +~~~~~~~~~~ + +``route_name`` + The name of the route, e.g. ``myroute``. This attribute is required. It + must be unique among all defined handler and route names in a given + configuration. + +``pattern`` + The pattern of the route e.g. ``ideas/{idea}``. This attribute is + required. See :ref:`route_pattern_syntax` for information about the syntax + of route patterns. The name ``{action}`` is treated specially in handler + patterns. See :ref:`using_add_handler` for a discussion of how + ``{action}`` in handler patterns is treated. + +``action`` + If the action name is not specified in the ``pattern``, use this name as the + handler action (method name). + +``factory`` + The :term:`dotted Python name` to a function that will generate a + :app:`Pyramid` context object when the associated route matches. + e.g. ``mypackage.models.MyFactoryClass``. If this argument is not + specified, a default root factory will be used. + +``xhr`` + This value should be either ``True`` or ``False``. If this value is + specified and is ``True``, the :term:`request` must possess an + ``HTTP_X_REQUESTED_WITH`` (aka ``X-Requested-With``) header for this + route to match. This is useful for detecting AJAX requests issued + from jQuery, Prototype and other Javascript libraries. If this + predicate returns false, route matching continues. + +``traverse`` + If you would like to cause the :term:`context` to be something other + than the :term:`root` object when this route matches, you can spell + a traversal pattern as the ``traverse`` argument. This traversal + pattern will be used as the traversal path: traversal will begin at + the root object implied by this route (either the global root, or + the object returned by the ``factory`` associated with this route). + + The syntax of the ``traverse`` argument is the same as it is for + ``pattern``. For example, if the ``pattern`` provided to the + ``route`` directive is ``articles/{article}/edit``, and the + ``traverse`` argument provided to the ``route`` directive is + ``/{article}``, when a request comes in that causes the route to + match in such a way that the ``article`` match value is '1' (when + the request URI is ``/articles/1/edit``), the traversal path will be + generated as ``/1``. This means that the root object's + ``__getitem__`` will be called with the name ``1`` during the + traversal phase. If the ``1`` object exists, it will become the + :term:`context` of the request. :ref:`traversal_chapter` has more + information about traversal. + + If the traversal path contains segment marker names which are not + present in the ``pattern`` argument, a runtime error will occur. + The ``traverse`` pattern should not contain segment markers that do + not exist in the ``pattern``. + + A similar combining of routing and traversal is available when a + route is matched which contains a ``*traverse`` remainder marker in + its ``pattern`` (see :ref:`using_traverse_in_a_route_pattern`). The + ``traverse`` argument to the ``route`` directive allows you to + associate route patterns with an arbitrary traversal path without + using a a ``*traverse`` remainder marker; instead you can use other + match information. + + Note that the ``traverse`` argument to the ``handler`` directive is + ignored when attached to a route that has a ``*traverse`` remainder + marker in its pattern. + +``request_method`` + A string representing an HTTP method name, e.g. ``GET``, ``POST``, + ``HEAD``, ``DELETE``, ``PUT``. If this argument is not specified, + this route will match if the request has *any* request method. If + this predicate returns false, route matching continues. + +``path_info`` + The value of this attribute represents a regular expression pattern + that will be tested against the ``PATH_INFO`` WSGI environment + variable. If the regex matches, this predicate will be true. If + this predicate returns false, route matching continues. + +``request_param`` + This value can be any string. A view declaration with this + attribute ensures that the associated route will only match when the + request has a key in the ``request.params`` dictionary (an HTTP + ``GET`` or ``POST`` variable) that has a name which matches the + supplied value. If the value supplied to the attribute has a ``=`` + sign in it, e.g. ``request_params="foo=123"``, then the key + (``foo``) must both exist in the ``request.params`` dictionary, and + the value must match the right hand side of the expression (``123``) + for the route to "match" the current request. If this predicate + returns false, route matching continues. + +``header`` + The value of this attribute represents an HTTP header name or a + header name/value pair. If the value contains a ``:`` (colon), it + will be considered a name/value pair (e.g. ``User-Agent:Mozilla/.*`` + or ``Host:localhost``). The *value* of an attribute that represent + a name/value pair should be a regular expression. If the value does + not contain a colon, the entire value will be considered to be the + header name (e.g. ``If-Modified-Since``). If the value evaluates to + a header name only without a value, the header specified by the name + must be present in the request for this predicate to be true. If + the value evaluates to a header name/value pair, the header + specified by the name must be present in the request *and* the + regular expression specified as the value must match the header + value. Whether or not the value represents a header name or a + header name/value pair, the case of the header name is not + significant. If this predicate returns false, route matching + continues. + +``accept`` + The value of this attribute represents a match query for one or more + mimetypes in the ``Accept`` HTTP request header. If this value is + specified, it must be in one of the following forms: a mimetype + match token in the form ``text/plain``, a wildcard mimetype match + token in the form ``text/*`` or a match-all wildcard mimetype match + token in the form ``*/*``. If any of the forms matches the + ``Accept`` header of the request, this predicate will be true. If + this predicate returns false, route matching continues. + +``custom_predicates`` + This value should be a sequence of references to custom predicate + callables. Use custom predicates when no set of predefined + predicates does what you need. Custom predicates can be combined + with predefined predicates as necessary. Each custom predicate + callable should accept two arguments: ``info`` and ``request`` + and should return either ``True`` or ``False`` after doing arbitrary + evaluation of the info and/or the request. If all custom and + non-custom predicate callables return ``True`` the associated route + will be considered viable for a given request. If any predicate + callable returns ``False``, route matching continues. Note that the + value ``info`` passed to a custom route predicate is a dictionary + containing matching information; see :ref:`custom_route_predicates` + for more information about ``info``. + + +Alternatives +~~~~~~~~~~~~ + +You can also add a :term:`route configuration` via: + +- Using the :meth:`pyramid.config.Configurator.add_handler` method. + +See Also +~~~~~~~~ + +See also :ref:`handlers_chapter`. diff --git a/docs/zcml/localenegotiator.rst b/docs/zcml/localenegotiator.rst index 100fa837a..c90e649f6 100644 --- a/docs/zcml/localenegotiator.rst +++ b/docs/zcml/localenegotiator.rst @@ -29,7 +29,7 @@ Example Alternatives ~~~~~~~~~~~~ -Use :meth:`pyramid.configuration.Configurator.set_locale_negotiator` +Use :meth:`pyramid.config.Configurator.set_locale_negotiator` method instance during initial application setup. See Also diff --git a/docs/zcml/notfound.rst b/docs/zcml/notfound.rst index 54826ea17..a2ed95bc4 100644 --- a/docs/zcml/notfound.rst +++ b/docs/zcml/notfound.rst @@ -67,7 +67,7 @@ Alternatives Use the :ref:`view_directive` directive with a ``context`` that names the :exc:`pyramid.exceptions.NotFound` class. -Use the :meth:`pyramid.configuration.Configurator.add_view` method, +Use the :meth:`pyramid.config.Configurator.add_view` method, passing it a ``context`` which is the :exc:`pyramid.exceptions.NotFound` class. diff --git a/docs/zcml/remoteuserauthenticationpolicy.rst b/docs/zcml/remoteuserauthenticationpolicy.rst index d21cd99d2..56e73ee7a 100644 --- a/docs/zcml/remoteuserauthenticationpolicy.rst +++ b/docs/zcml/remoteuserauthenticationpolicy.rst @@ -40,7 +40,7 @@ Alternatives You may create an instance of the :class:`pyramid.authentication.RemoteUserAuthenticationPolicy` and -pass it to the :class:`pyramid.configuration.Configurator` +pass it to the :class:`pyramid.config.Configurator` constructor as the ``authentication_policy`` argument during initial application configuration. diff --git a/docs/zcml/renderer.rst b/docs/zcml/renderer.rst index 67c2a7f0c..c7beead32 100644 --- a/docs/zcml/renderer.rst +++ b/docs/zcml/renderer.rst @@ -42,7 +42,7 @@ Examples Alternatives ~~~~~~~~~~~~ -The :meth:`pyramid.configuration.Configurator.add_renderer` method +The :meth:`pyramid.config.Configurator.add_renderer` method is equivalent to the ``renderer`` ZCML directive. See Also diff --git a/docs/zcml/repozewho1authenticationpolicy.rst b/docs/zcml/repozewho1authenticationpolicy.rst index e5152f2e1..11907ce31 100644 --- a/docs/zcml/repozewho1authenticationpolicy.rst +++ b/docs/zcml/repozewho1authenticationpolicy.rst @@ -42,7 +42,7 @@ Alternatives You may create an instance of the :class:`pyramid.authentication.RepozeWho1AuthenticationPolicy` and -pass it to the :class:`pyramid.configuration.Configurator` +pass it to the :class:`pyramid.config.Configurator` constructor as the ``authentication_policy`` argument during initial application configuration. diff --git a/docs/zcml/resource.rst b/docs/zcml/resource.rst index 4cf5ef400..3f7c58faa 100644 --- a/docs/zcml/resource.rst +++ b/docs/zcml/resource.rst @@ -53,7 +53,7 @@ Examples Alternatives ~~~~~~~~~~~~ -The :meth:`pyramid.configuration.Configurator.override_resource` +The :meth:`pyramid.config.Configurator.override_resource` method can be used instead of the ``resource`` ZCML directive. See Also diff --git a/docs/zcml/route.rst b/docs/zcml/route.rst index c3bec72df..4f7cdb955 100644 --- a/docs/zcml/route.rst +++ b/docs/zcml/route.rst @@ -215,7 +215,7 @@ Alternatives You can also add a :term:`route configuration` via: -- Using the :meth:`pyramid.configuration.Configurator.add_route` method. +- Using the :meth:`pyramid.config.Configurator.add_route` method. See Also ~~~~~~~~ diff --git a/docs/zcml/scan.rst b/docs/zcml/scan.rst index cbf797e1c..df2cdd281 100644 --- a/docs/zcml/scan.rst +++ b/docs/zcml/scan.rst @@ -25,7 +25,7 @@ Example Alternatives ~~~~~~~~~~~~ -The :meth:`pyramid.configuration.Configurator.scan` method performs +The :meth:`pyramid.config.Configurator.scan` method performs the same job as the ``scan`` ZCML directive. See Also diff --git a/docs/zcml/static.rst b/docs/zcml/static.rst index 164e7400c..9538d18f0 100644 --- a/docs/zcml/static.rst +++ b/docs/zcml/static.rst @@ -79,7 +79,7 @@ Examples Alternatives ~~~~~~~~~~~~ -:meth:`pyramid.configuration.configurator.add_static_view` can also +:meth:`pyramid.config.Configurator.add_static_view` can also be used to add a static view. See Also diff --git a/docs/zcml/subscriber.rst b/docs/zcml/subscriber.rst index d584f1e02..25c4abf2e 100644 --- a/docs/zcml/subscriber.rst +++ b/docs/zcml/subscriber.rst @@ -37,7 +37,7 @@ Alternatives ~~~~~~~~~~~~ You can also register an event listener by using the -:meth:`pyramid.configuration.Configurator.add_subscriber` method. +:meth:`pyramid.config.Configurator.add_subscriber` method. See Also ~~~~~~~~ diff --git a/docs/zcml/translationdir.rst b/docs/zcml/translationdir.rst index 56c0977db..5cf615d26 100644 --- a/docs/zcml/translationdir.rst +++ b/docs/zcml/translationdir.rst @@ -55,7 +55,7 @@ Example 3 Alternatives ~~~~~~~~~~~~ -Use :meth:`pyramid.configuration.Configurator.add_translation_dirs` +Use :meth:`pyramid.config.Configurator.add_translation_dirs` method instance during initial application setup. See Also diff --git a/docs/zcml/view.rst b/docs/zcml/view.rst index 6e67b4c56..74d497cb3 100644 --- a/docs/zcml/view.rst +++ b/docs/zcml/view.rst @@ -244,7 +244,7 @@ You can also add a :term:`view configuration` via: - Using the :class:`pyramid.view.view_config` class as a decorator. -- Using the :meth:`pyramid.configuration.Configurator.add_view` method. +- Using the :meth:`pyramid.config.Configurator.add_view` method. See Also ~~~~~~~~ diff --git a/pyramid/config.py b/pyramid/config.py new file mode 100644 index 000000000..72561c493 --- /dev/null +++ b/pyramid/config.py @@ -0,0 +1,2942 @@ +import inspect +import os +import re +import sys +import threading +import traceback + +import venusian + +from translationstring import ChameleonTranslate + +from zope.configuration import xmlconfig +from zope.configuration.config import GroupingContextDecorator +from zope.configuration.config import ConfigurationMachine +from zope.configuration.xmlconfig import registerCommonDirectives + +from zope.interface import Interface +from zope.interface import implementedBy +from zope.interface.interfaces import IInterface +from zope.interface import implements + +from pyramid.interfaces import IAuthenticationPolicy +from pyramid.interfaces import IAuthorizationPolicy +from pyramid.interfaces import IChameleonTranslate +from pyramid.interfaces import IDebugLogger +from pyramid.interfaces import IDefaultPermission +from pyramid.interfaces import IDefaultRootFactory +from pyramid.interfaces import IException +from pyramid.interfaces import IExceptionResponse +from pyramid.interfaces import IExceptionViewClassifier +from pyramid.interfaces import ILocaleNegotiator +from pyramid.interfaces import IMultiView +from pyramid.interfaces import IPackageOverrides +from pyramid.interfaces import IRendererFactory +from pyramid.interfaces import IRendererGlobalsFactory +from pyramid.interfaces import IRequest +from pyramid.interfaces import IRequestFactory +from pyramid.interfaces import IRootFactory +from pyramid.interfaces import IRouteRequest +from pyramid.interfaces import IRoutesMapper +from pyramid.interfaces import ISecuredView +from pyramid.interfaces import ISessionFactory +from pyramid.interfaces import IStaticURLInfo +from pyramid.interfaces import ITranslationDirectories +from pyramid.interfaces import ITraverser +from pyramid.interfaces import IView +from pyramid.interfaces import IViewClassifier + +try: + from pyramid import chameleon_text +except TypeError: # pragma: no cover + chameleon_text = None # pypy +try: + from pyramid import chameleon_zpt +except TypeError: # pragma: no cover + chameleon_zpt = None # pypy + +from pyramid import renderers +from pyramid.authorization import ACLAuthorizationPolicy +from pyramid.compat import all +from pyramid.compat import md5 +from pyramid.events import ApplicationCreated +from pyramid.exceptions import ConfigurationError +from pyramid.exceptions import Forbidden +from pyramid.exceptions import NotFound +from pyramid.exceptions import PredicateMismatch +from pyramid.i18n import get_localizer +from pyramid.log import make_stream_logger +from pyramid.mako_templating import renderer_factory as mako_renderer_factory +from pyramid.path import caller_package +from pyramid.path import package_path +from pyramid.path import package_of +from pyramid.registry import Registry +from pyramid.renderers import RendererHelper +from pyramid.request import route_request_iface +from pyramid.resource import PackageOverrides +from pyramid.resource import resolve_resource_spec +from pyramid.settings import Settings +from pyramid.static import StaticURLInfo +from pyramid.threadlocal import get_current_registry +from pyramid.threadlocal import get_current_request +from pyramid.threadlocal import manager +from pyramid.traversal import DefaultRootFactory +from pyramid.traversal import find_interface +from pyramid.traversal import traversal_path +from pyramid.urldispatch import RoutesMapper +from pyramid.util import DottedNameResolver +from pyramid.view import default_exceptionresponse_view +from pyramid.view import render_view_to_response + +MAX_ORDER = 1 << 30 +DEFAULT_PHASH = md5().hexdigest() + +DEFAULT_RENDERERS = ( + ('.mak', mako_renderer_factory), + ('.mako', mako_renderer_factory), + ('json', renderers.json_renderer_factory), + ('string', renderers.string_renderer_factory), + ) + +if chameleon_text: + DEFAULT_RENDERERS += (('.pt', chameleon_zpt.renderer_factory),) +if chameleon_zpt: + DEFAULT_RENDERERS += (('.txt', chameleon_text.renderer_factory),) + +class Configurator(object): + """ + A Configurator is used to configure a :app:`Pyramid` + :term:`application registry`. + + The Configurator accepts a number of arguments: ``registry``, + ``package``, ``settings``, ``root_factory``, + ``authentication_policy``, ``authorization_policy``, ``renderers`` + ``debug_logger``, ``locale_negotiator``, ``request_factory``, and + ``renderer_globals_factory``. + + If the ``registry`` argument is passed as a non-``None`` value, it + must be an instance of the :class:`pyramid.registry.Registry` + class representing the registry to configure. If ``registry`` is + ``None``, the configurator will create a + :class:`pyramid.registry.Registry` instance itself; it will + also perform some default configuration that would not otherwise + be done. After construction, the configurator may be used to add + configuration to the registry. The overall state of a registry is + called the 'configuration state'. + + .. warning:: If a ``registry`` is passed to the Configurator + constructor, all other constructor arguments except ``package`` + are ignored. + + If the ``package`` argument is passed, it must be a reference to a + Python :term:`package` (e.g. ``sys.modules['thepackage']``) or a + :term:`dotted Python name` to same. This value is used as a basis + to convert relative paths passed to various configuration methods, + such as methods which accept a ``renderer`` argument, into + absolute paths. If ``None`` is passed (the default), the package + is assumed to be the Python package in which the *caller* of the + ``Configurator`` constructor lives. + + If the ``settings`` argument is passed, it should be a Python dictionary + representing the deployment settings for this application. These are + later retrievable using the :attr:`pyramid.registry.Registry.settings` + attribute (aka ``request.registry.settings``). + + If the ``root_factory`` argument is passed, it should be an object + representing the default :term:`root factory` for your application + or a :term:`dotted Python name` to same. If it is ``None``, a + default root factory will be used. + + If ``authentication_policy`` is passed, it should be an instance + of an :term:`authentication policy` or a :term:`dotted Python + name` to same. + + If ``authorization_policy`` is passed, it should be an instance of + an :term:`authorization policy` or a :term:`dotted Python name` to + same. + + .. note:: A ``ConfigurationError`` will be raised when an + authorization policy is supplied without also supplying an + authentication policy (authorization requires authentication). + + If ``renderers`` is passed, it should be a list of tuples + representing a set of :term:`renderer` factories which should be + configured into this application (each tuple representing a set of + positional values that should be passed to + :meth:`pyramid.config.Configurator.add_renderer`). If + it is not passed, a default set of renderer factories is used. + + If ``debug_logger`` is not passed, a default debug logger that + logs to stderr will be used. If it is passed, it should be an + instance of the :class:`logging.Logger` (PEP 282) standard library + class or a :term:`dotted Python name` to same. The debug logger + is used by :app:`Pyramid` itself to log warnings and + authorization debugging information. + + If ``locale_negotiator`` is passed, it should be a :term:`locale + negotiator` implementation or a :term:`dotted Python name` to + same. See :ref:`custom_locale_negotiator`. + + If ``request_factory`` is passed, it should be a :term:`request + factory` implementation or a :term:`dotted Python name` to same. + See :ref:`custom_request_factory`. By default it is ``None``, + which means use the default request factory. + + If ``renderer_globals_factory`` is passed, it should be a + :term:`renderer globals` factory implementation or a :term:`dotted + Python name` to same. See :ref:`custom_renderer_globals_factory`. + By default, it is ``None``, which means use no renderer globals + factory. + + If ``default_permission`` is passed, it should be a + :term:`permission` string to be used as the default permission for + all view configuration registrations performed against this + Configurator. An example of a permission string:``'view'``. + Adding a default permission makes it unnecessary to protect each + view configuration with an explicit permission, unless your + application policy requires some exception for a particular view. + By default, ``default_permission`` is ``None``, meaning that view + configurations which do not explicitly declare a permission will + always be executable by entirely anonymous users (any + authorization policy in effect is ignored). See also + :ref:`setting_a_default_permission`. + + If ``session_factory`` is passed, it should be an object which + implements the :term:`session factory` interface. If a nondefault + value is passed, the ``session_factory`` will be used to create a + session object when ``request.session`` is accessed. Note that + the same outcome can be achieved by calling + :meth:`pyramid.config.Configurator.set_session_factory`. By + default, this argument is ``None``, indicating that no session + factory will be configured (and thus accessing ``request.session`` + will throw an error) unless ``set_session_factory`` is called later + during configuration. + + If ``autocommit`` is ``True``, every method called on the configurator + will cause an immediate action, and no configuration conflict detection + will be used. If ``autocommit`` is ``False``, most methods of the + configurator will defer their action until + :meth:`pyramid.config.Configurator.commit` is called. When + :meth:`pyramid.config.Configurator.commit` is called, the actions implied + by the called methods will be checked for configuration conflicts unless + ``autocommit`` is ``True``. If a conflict is detected a + ``ConfigurationConflictError`` will be raised. Calling + :meth:`pyramid.config.Configurator.make_wsgi_app` always implies a final + commit.""" + + manager = manager # for testing injection + venusian = venusian # for testing injection + _ctx = None + + def __init__(self, + registry=None, + package=None, + settings=None, + root_factory=None, + authentication_policy=None, + authorization_policy=None, + renderers=DEFAULT_RENDERERS, + debug_logger=None, + locale_negotiator=None, + request_factory=None, + renderer_globals_factory=None, + default_permission=None, + session_factory=None, + autocommit=False, + ): + if package is None: + package = caller_package() + name_resolver = DottedNameResolver(package) + self.name_resolver = name_resolver + self.package_name = name_resolver.package_name + self.package = name_resolver.package + self.registry = registry + self.autocommit = autocommit + if registry is None: + registry = Registry(self.package_name) + self.registry = registry + self.setup_registry( + settings=settings, + root_factory=root_factory, + authentication_policy=authentication_policy, + authorization_policy=authorization_policy, + renderers=renderers, + debug_logger=debug_logger, + locale_negotiator=locale_negotiator, + request_factory=request_factory, + renderer_globals_factory=renderer_globals_factory, + default_permission=default_permission, + session_factory=session_factory, + ) + + def _set_settings(self, mapping): + settings = Settings(mapping or {}) + self.registry.settings = settings + return settings + + #@action_method + def _set_root_factory(self, factory): + """ Add a :term:`root factory` to the current configuration + state. If the ``factory`` argument is ``None`` a default root + factory will be registered.""" + factory = self.maybe_dotted(factory) + if factory is None: + factory = DefaultRootFactory + def register(): + self.registry.registerUtility(factory, IRootFactory) + self.registry.registerUtility(factory, IDefaultRootFactory) # b/c + self.action(IRootFactory, register) + + #@action_method + def _set_authentication_policy(self, policy): + """ Add a :app:`Pyramid` :term:`authentication policy` to + the current configuration.""" + policy = self.maybe_dotted(policy) + self.registry.registerUtility(policy, IAuthenticationPolicy) + self.action(IAuthenticationPolicy) + + #@action_method + def _set_authorization_policy(self, policy): + """ Add a :app:`Pyramid` :term:`authorization policy` to + the current configuration state (also accepts a :term:`dotted + Python name`.""" + policy = self.maybe_dotted(policy) + self.registry.registerUtility(policy, IAuthorizationPolicy) + self.action(IAuthorizationPolicy, None) + + def _make_spec(self, path_or_spec): + package, filename = resolve_resource_spec(path_or_spec, + self.package_name) + if package is None: + return filename # absolute filename + return '%s:%s' % (package, filename) + + def _split_spec(self, path_or_spec): + return resolve_resource_spec(path_or_spec, self.package_name) + + def _derive_view(self, view, permission=None, predicates=(), + attr=None, renderer=None, wrapper_viewname=None, + viewname=None, accept=None, order=MAX_ORDER, + phash=DEFAULT_PHASH): + if renderer is None: # use default renderer if one exists + default_renderer_factory = self.registry.queryUtility( + IRendererFactory) + if default_renderer_factory is not None: + renderer = {'name':None, 'package':self.package} + view = self.maybe_dotted(view) + authn_policy = self.registry.queryUtility(IAuthenticationPolicy) + authz_policy = self.registry.queryUtility(IAuthorizationPolicy) + settings = self.registry.settings + logger = self.registry.queryUtility(IDebugLogger) + mapped_view = _map_view(view, self.registry, attr, renderer) + owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname) + secured_view = _secure_view(owrapped_view, permission, + authn_policy, authz_policy) + debug_view = _authdebug_view(secured_view, permission, + authn_policy, authz_policy, settings, + logger) + predicated_view = _predicate_wrap(debug_view, predicates) + derived_view = _attr_wrap(predicated_view, accept, order, phash) + return derived_view + + 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) + + def _set_security_policies(self, authentication, authorization=None): + if authorization is None: + authorization = ACLAuthorizationPolicy() # default + if authorization and not authentication: + raise ConfigurationError( + 'If the "authorization" is passed a value, ' + 'the "authentication" argument must also be ' + 'passed a value; authorization requires authentication.') + self._set_authentication_policy(authentication) + self._set_authorization_policy(authorization) + + def _fix_registry(self): + """ Fix up a ZCA component registry that is not a + pyramid.registry.Registry by adding analogues of ``has_listeners``, + and ``notify`` through monkey-patching.""" + + _registry = self.registry + + if not hasattr(_registry, 'notify'): + def notify(*events): + [ _ for _ in _registry.subscribers(events, None) ] + _registry.notify = notify + + if not hasattr(_registry, 'has_listeners'): + _registry.has_listeners = True + + def _make_context(self, autocommit=False): + context = PyramidConfigurationMachine() + registerCommonDirectives(context) + context.registry = self.registry + context.autocommit = autocommit + return context + + # API + + def action(self, discriminator, callable=None, args=(), kw=None, order=0): + """ Register an action which will be executed when + :meth:`pyramid.config.Configuration.commit` is called (or executed + immediately if ``autocommit`` is ``True``). + + .. note:: This method is typically only used by :app:`Pyramid` + framework extension authors, not by :app:`Pyramid` application + developers. + + The ``discriminator`` uniquely identifies the action. It must be + given, but it can be ``None``, to indicate that the action never + conflicts. It must be a hashable value. + + The ``callable`` is a callable object which performs the action. It + is optional. ``args`` and ``kw`` are tuple and dict objects + respectively, which are passed to ``callable`` when this action is + executed. + + ``order`` is a crude order control mechanism, only rarely used (has + no effect when autocommit is ``True``). + """ + if kw is None: + kw = {} + + context = self._ctx + + if context is None: + autocommit = self.autocommit + else: + autocommit = context.autocommit + + if autocommit: + if callable is not None: + callable(*args, **kw) + else: + if context is None: # defer expensive creation of context + context = self._ctx = self._make_context(self.autocommit) + if not context.info: + # Try to provide more accurate info for conflict reports by + # wrapping the context in a decorator and attaching caller info + # to it, unless the context already has info (if it already has + # info, it's likely a context generated by a ZCML directive). + context = GroupingContextDecorator(context) + try: + f = traceback.extract_stack(limit=3) + info = f[-3] + except: # pragma: no cover + info = '' + context.info = info + context.action(discriminator, callable, args, kw, order) + + def commit(self): + """ Commit any pending configuration actions. If a configuration + conflict is detected in the pending configuration actins, this method + will raise a :exc:`ConfigurationConflictError`; within the traceback + of this error will be information about the source of the conflict, + usually including file names and line numbers of the cause of the + configuration conflicts.""" + if self._ctx is None: + return + self._ctx.execute_actions() + # unwrap and reset the context + self._ctx = None + + def include(self, *callables): + """Include one or more configuration callables, to support imperative + application extensibility. + + A configuration callable should be a callable that accepts a single + argument named ``config``, which will be an instance of a + :term:`Configurator` (be warned that it will not be the same + configurator instance on which you call this method, however). The + code which runs as the result of calling the callable should invoke + methods on the configurator passed to it which add configuration + state. The return value of a callable will be ignored. + + Values allowed to be presented via the ``*callables`` argument to + this method: any callable Python object or any :term:`dotted Python + name` which resolves to a callable Python object. + + For example, if the ``configure`` function below lives in a module + named ``myapp.myconfig``: + + .. code-block:: python + :linenos: + + # myapp.myconfig module + + def my_view(request): + from pyramid.response import Response + return Response('OK') + + def configure(config): + config.add_view(my_view) + + You might cause it be included within your Pyramid application like + so: + + .. code-block:: python + :linenos: + + from pyramid.config import Configurator + + def main(global_config, **settings): + config = Configurator() + config.include('myapp.myconfig.configure') + + Included configuration statements will be overridden by local + configuration statements if an included callable causes a + configuration conflict by registering something with the same + configuration parameters.""" + + _context = self._ctx + if _context is None: + _context = self._ctx = self._make_context(self.autocommit) + + for c in callables: + c = self.maybe_dotted(c) + sourcefile = inspect.getsourcefile(c) + module = inspect.getmodule(c) + spec = module.__name__ + ':' + c.__name__ + if _context.processSpec(spec): + context = GroupingContextDecorator(_context) + context.basepath = os.path.dirname(sourcefile) + context.includepath = _context.includepath + (spec,) + context.package = package_of(module) + config = self.__class__.with_context(context) + c(config) + + @classmethod + def with_context(cls, context): + """A classmethod used by ZCML directives, + :meth:`pyramid.config.Configurator.with_package`, and + :meth:`pyramid.config.Configurator.include` to obtain a configurator + with 'the right' context. Returns a new Configurator instance.""" + configurator = cls(registry=context.registry, package=context.package, + autocommit=context.autocommit) + configurator._ctx = context + return configurator + + def with_package(self, package): + """ Return a new Configurator instance with the same registry + as this configurator using the package supplied as the + ``package`` argument to the new configurator. ``package`` may + be an actual Python package object or a Python dotted name + representing a package.""" + context = self._ctx + if context is None: + context = self._ctx = self._make_context(self.autocommit) + context = GroupingContextDecorator(context) + context.package = package + return self.__class__.with_context(context) + + def maybe_dotted(self, dotted): + """ Resolve the :term:`dotted Python name` ``dotted`` to a + global Python object. If ``dotted`` is not a string, return + it without attempting to do any name resolution. If + ``dotted`` is a relative dotted name (e.g. ``.foo.bar``, + consider it relative to the ``package`` argument supplied to + this Configurator's constructor.""" + return self.name_resolver.maybe_resolve(dotted) + + def absolute_resource_spec(self, relative_spec): + """ Resolve the potentially relative :term:`resource + specification` string passed as ``relative_spec`` into an + absolute resource specification string and return the string. + Use the ``package`` of this configurator as the package to + which the resource specification will be considered relative + when generating an absolute resource specification. If the + provided ``relative_spec`` argument is already absolute, or if + the ``relative_spec`` is not a string, it is simply returned.""" + if not isinstance(relative_spec, basestring): + return relative_spec + return self._make_spec(relative_spec) + + def setup_registry(self, settings=None, root_factory=None, + authentication_policy=None, authorization_policy=None, + renderers=DEFAULT_RENDERERS, debug_logger=None, + locale_negotiator=None, request_factory=None, + renderer_globals_factory=None, + default_permission=None, + session_factory=None): + """ When you pass a non-``None`` ``registry`` argument to the + :term:`Configurator` constructor, no initial 'setup' is performed + against the registry. This is because the registry you pass in may + have already been initialized for use under :app:`Pyramid` via a + different configurator. However, in some circumstances (such as when + you want to use the Zope 'global` registry instead of a registry + created as a result of the Configurator constructor), or when you + want to reset the initial setup of a registry, you *do* want to + explicitly initialize the registry associated with a Configurator for + use under :app:`Pyramid`. Use ``setup_registry`` to do this + initialization. + + ``setup_registry`` configures settings, a root factory, security + policies, renderers, a debug logger, a locale negotiator, and various + other settings using the configurator's current registry, as per the + descriptions in the Configurator constructor.""" + registry = self.registry + self._fix_registry() + self._set_settings(settings) + self._set_root_factory(root_factory) + debug_logger = self.maybe_dotted(debug_logger) + if debug_logger is None: + debug_logger = make_stream_logger('pyramid.debug', sys.stderr) + registry.registerUtility(debug_logger, IDebugLogger) + if authentication_policy or authorization_policy: + self._set_security_policies(authentication_policy, + authorization_policy) + for name, renderer in renderers: + self.add_renderer(name, renderer) + self.add_view(default_exceptionresponse_view, + context=IExceptionResponse) + if locale_negotiator: + locale_negotiator = self.maybe_dotted(locale_negotiator) + registry.registerUtility(locale_negotiator, ILocaleNegotiator) + if request_factory: + request_factory = self.maybe_dotted(request_factory) + self.set_request_factory(request_factory) + if renderer_globals_factory: + renderer_globals_factory = self.maybe_dotted( + renderer_globals_factory) + self.set_renderer_globals_factory(renderer_globals_factory) + if default_permission: + self.set_default_permission(default_permission) + if session_factory is not None: + self.set_session_factory(session_factory) + self.commit() + + # getSiteManager is a unit testing dep injection + def hook_zca(self, getSiteManager=None): + """ Call :func:`zope.component.getSiteManager.sethook` with + the argument + :data:`pyramid.threadlocal.get_current_registry`, causing + the :term:`Zope Component Architecture` 'global' APIs such as + :func:`zope.component.getSiteManager`, + :func:`zope.component.getAdapter` and others to use the + :app:`Pyramid` :term:`application registry` rather than the + Zope 'global' registry. If :mod:`zope.component` cannot be + imported, this method will raise an :exc:`ImportError`.""" + if getSiteManager is None: + from zope.component import getSiteManager + getSiteManager.sethook(get_current_registry) + + # getSiteManager is a unit testing dep injection + def unhook_zca(self, getSiteManager=None): + """ Call :func:`zope.component.getSiteManager.reset` to undo + the action of + :meth:`pyramid.config.Configurator.hook_zca`. If + :mod:`zope.component` cannot be imported, this method will + raise an :exc:`ImportError`.""" + if getSiteManager is None: # pragma: no cover + from zope.component import getSiteManager + getSiteManager.reset() + + def begin(self, request=None): + """ Indicate that application or test configuration has begun. + This pushes a dictionary containing the :term:`application + registry` implied by ``registry`` attribute of this + configurator and the :term:`request` implied by the + ``request`` argument on to the :term:`thread local` stack + consulted by various :mod:`pyramid.threadlocal` API + functions.""" + self.manager.push({'registry':self.registry, 'request':request}) + + def end(self): + """ Indicate that application or test configuration has ended. + This pops the last value pushed on to the :term:`thread local` + stack (usually by the ``begin`` method) and returns that + value. + """ + return self.manager.pop() + + def derive_view(self, view, attr=None, renderer=None): + """ + + Create a :term:`view callable` using the function, instance, + or class (or :term:`dotted Python name` referring to the same) + provided as ``view`` object. + + This is API is useful to framework extenders who create + pluggable systems which need to register 'proxy' view + callables for functions, instances, or classes which meet the + requirements of being a :app:`Pyramid` view callable. For + example, a ``some_other_framework`` function in another + framework may want to allow a user to supply a view callable, + but he may want to wrap the view callable in his own before + registering the wrapper as a :app:`Pyramid` view callable. + Because a :app:`Pyramid` view callable can be any of a + number of valid objects, the framework extender will not know + how to call the user-supplied object. Running it through + ``derive_view`` normalizes it to a callable which accepts two + arguments: ``context`` and ``request``. + + For example: + + .. code-block:: python + + def some_other_framework(user_supplied_view): + config = Configurator(reg) + proxy_view = config.derive_view(user_supplied_view) + def my_wrapper(context, request): + do_something_that_mutates(request) + return proxy_view(context, request) + config.add_view(my_wrapper) + + The ``view`` object provided should be one of the following: + + - A function or another non-class callable object that accepts + a :term:`request` as a single positional argument and which + returns a :term:`response` object. + + - A function or other non-class callable object that accepts + two positional arguments, ``context, request`` and which + returns a :term:`response` object. + + - A class which accepts a single positional argument in its + constructor named ``request``, and which has a ``__call__`` + method that accepts no arguments that returns a + :term:`response` object. + + - A class which accepts two positional arguments named + ``context, request``, and which has a ``__call__`` method + that accepts no arguments that returns a :term:`response` + object. + + - A :term:`dotted Python name` which refers to any of the + kinds of objects above. + + This API returns a callable which accepts the arguments + ``context, request`` and which returns the result of calling + the provided ``view`` object. + + The ``attr`` keyword argument is most useful when the view + object is a class. It names the method that should be used as + the callable. If ``attr`` is not provided, the attribute + effectively defaults to ``__call__``. See + :ref:`class_as_view` for more information. + + The ``renderer`` keyword argument should be a renderer + name. If supplied, it will cause the returned callable to use + a :term:`renderer` to convert the user-supplied view result to + a :term:`response` object. If a ``renderer`` argument is not + supplied, the user-supplied view must itself return a + :term:`response` object. """ + + if renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + return self._derive_view(view, attr=attr, renderer=renderer) + + #@action_method + def add_subscriber(self, subscriber, iface=None): + """Add an event :term:`subscriber` for the event stream + implied by the supplied ``iface`` interface. The + ``subscriber`` argument represents a callable object (or a + :term:`dotted Python name` which identifies a callable); it + will be called with a single object ``event`` whenever + :app:`Pyramid` emits an :term:`event` associated with the + ``iface``, which may be an :term:`interface` or a class or a + :term:`dotted Python name` to a global object representing an + interface or a class. Using the default ``iface`` value, + ``None`` will cause the subscriber to be registered for all + event types. See :ref:`events_chapter` for more information + about events and subscribers.""" + dotted = self.maybe_dotted + subscriber, iface = dotted(subscriber), dotted(iface) + if iface is None: + iface = (Interface,) + if not isinstance(iface, (tuple, list)): + iface = (iface,) + def register(): + self.registry.registerHandler(subscriber, iface) + self.action(None, register) + return subscriber + + def add_settings(self, settings=None, **kw): + """Augment the ``settings`` argument passed in to the Configurator + constructor with one or more 'setting' key/value pairs. A setting is + a single key/value pair in the dictionary-ish object returned from + the API :attr:`pyramid.registry.Registry.settings` and + :meth:`pyramid.config.Configurator.get_settings`. + + You may pass a dictionary:: + + config.add_settings({'external_uri':'http://example.com'}) + + Or a set of key/value pairs:: + + config.add_settings(external_uri='http://example.com') + + This function is useful when you need to test code that accesses the + :attr:`pyramid.registry.Registry.settings` API (or the + :meth:`pyramid.config.Configurator.get_settings` API) and + which uses values from that API. + """ + if settings is None: + settings = {} + utility = self.registry.settings + if utility is None: + utility = self._set_settings(settings) + utility.update(settings) + utility.update(kw) + + def get_settings(self): + """ + Return a 'settings' object for the current application. A + 'settings' object is a dictionary-like object that contains + key/value pairs based on the dictionary passed as the ``settings`` + argument to the :class:`pyramid.config.Configurator` + constructor or the :func:`pyramid.router.make_app` API. + + .. note:: For backwards compatibility, dictionary keys can also be + looked up as attributes of the settings object. + + .. note:: the :attr:`pyramid.registry.Registry.settings` API + performs the same duty. + """ + return self.registry.settings + + def make_wsgi_app(self): + """ Returns a :app:`Pyramid` WSGI application representing + the current configuration state and sends a + :class:`pyramid.events.ApplicationCreated` + event to all listeners.""" + self.commit() + from pyramid.router import Router # avoid circdep + app = Router(self.registry) + # We push the registry on to the stack here in case any code + # that depends on the registry threadlocal APIs used in + # listeners subscribed to the IApplicationCreated event. + self.manager.push({'registry':self.registry, 'request':None}) + try: + self.registry.notify(ApplicationCreated(app)) + finally: + self.manager.pop() + return app + + #@action_method + def load_zcml(self, spec='configure.zcml', lock=threading.Lock()): + """ Load configuration from a :term:`ZCML` file into the + current configuration state. The ``spec`` argument is an + absolute filename, a relative filename, or a :term:`resource + specification`, defaulting to ``configure.zcml`` (relative to + the package of the configurator's caller).""" + package_name, filename = self._split_spec(spec) + if package_name is None: # absolute filename + package = self.package + else: + __import__(package_name) + package = sys.modules[package_name] + + registry = self.registry + self.manager.push({'registry':registry, 'request':None}) + context = self._ctx + if context is None: + context = self._ctx = self._make_context(self.autocommit) + + # To avoid breaking people's expectations of how ZCML works, we + # cannot autocommit ZCML actions incrementally. If we commit actions + # incrementally, configuration outcome will be controlled purely by + # ZCML directive execution order, which isn't what anyone who uses + # ZCML expects. So we don't autocommit each ZCML directive action + # while parsing is happening, but we do make sure to pass + # execute=self.autocommit to xmlconfig.file below, which will cause + # the actions implied by the ZCML that was parsed to be committed + # right away once parsing is finished if autocommit is True. + context = GroupingContextDecorator(context) + context.autocommit = False + + lock.acquire() + try: + context.package = package + xmlconfig.file(filename, package, context=context, + execute=self.autocommit) + finally: + lock.release() + self.manager.pop() + return registry + + def add_handler(self, route_name, pattern, handler, action=None, **kw): + + """ Add a Pylons-style view handler. This function adds a + route and some number of views based on a handler object + (usually a class). + + ``route_name`` is the name of the route (to be used later in + URL generation). + + ``pattern`` is the matching pattern, + e.g. ``'/blog/{action}'``. ``pattern`` may be ``None``, in + which case the pattern of an existing route named the same as + ``route_name`` is used. If ``pattern`` is ``None`` and no + route named ``route_name`` exists, a ``ConfigurationError`` is + raised. + + ``handler`` is a dotted name of (or direct reference to) a + Python handler class, + e.g. ``'my.package.handlers.MyHandler'``. + + If ``{action}`` or ``:action`` is in + the pattern, the exposed methods of the handler will be used + as views. + + If ``action`` is passed, it will be considered the method name + of the handler to use as a view. + + Passing both ``action`` and having an ``{action}`` in the + route pattern is disallowed. + + Any extra keyword arguments are passed along to ``add_route``. + + See :ref:`handlers_chapter` for more explanatory documentation. + + This method returns the result of add_route.""" + handler = self.maybe_dotted(handler) + + if pattern is not None: + route = self.add_route(route_name, pattern, **kw) + else: + mapper = self.get_routes_mapper() + route = mapper.get_route(route_name) + if route is None: + raise ConfigurationError( + 'The "pattern" parameter may only be "None" when a route ' + 'with the route_name argument was previously registered. ' + 'No such route named %r exists' % route_name) + + pattern = route.pattern + + path_has_action = ':action' in pattern or '{action}' in pattern + + if action and path_has_action: + raise ConfigurationError( + 'action= (%r) disallowed when an action is in the route ' + 'path %r' % (action, pattern)) + + if path_has_action: + autoexpose = getattr(handler, '__autoexpose__', r'[A-Za-z]+') + if autoexpose: + try: + autoexpose = re.compile(autoexpose).match + except (re.error, TypeError), why: + raise ConfigurationError(why[0]) + for method_name, method in inspect.getmembers( + handler, inspect.ismethod): + configs = getattr(method, '__exposed__', []) + if autoexpose and not configs: + if autoexpose(method_name): + configs = [{}] + for expose_config in configs: + # we don't want to mutate any dict in __exposed__, + # so we copy each + view_args = expose_config.copy() + action = view_args.pop('name', method_name) + preds = list(view_args.pop('custom_predicates', [])) + preds.append(ActionPredicate(action)) + view_args['custom_predicates'] = preds + self.add_view(view=handler, attr=method_name, + route_name=route_name, **view_args) + else: + method_name = action + if method_name is None: + method_name = '__call__' + + # Scan the controller for any other methods with this action name + for meth_name, method in inspect.getmembers( + handler, inspect.ismethod): + configs = getattr(method, '__exposed__', [{}]) + for expose_config in configs: + # Don't re-register the same view if this method name is + # the action name + if meth_name == action: + continue + # We only reg a view if the name matches the action + if expose_config.get('name') != method_name: + continue + # we don't want to mutate any dict in __exposed__, + # so we copy each + view_args = expose_config.copy() + del view_args['name'] + self.add_view(view=handler, attr=meth_name, + route_name=route_name, **view_args) + + # Now register the method itself + method = getattr(handler, method_name, None) + configs = getattr(method, '__exposed__', [{}]) + for expose_config in configs: + self.add_view(view=handler, attr=action, route_name=route_name, + **expose_config) + + return route + + #@action_method + def add_view(self, view=None, name="", for_=None, permission=None, + request_type=None, route_name=None, request_method=None, + request_param=None, containment=None, attr=None, + renderer=None, wrapper=None, xhr=False, accept=None, + header=None, path_info=None, custom_predicates=(), + context=None): + """ Add a :term:`view configuration` to the current + configuration state. Arguments to ``add_view`` are broken + down below into *predicate* arguments and *non-predicate* + arguments. Predicate arguments narrow the circumstances in + which the view callable will be invoked when a request is + presented to :app:`Pyramid`; non-predicate arguments are + informational. + + Non-Predicate Arguments + + view + + A :term:`view callable` or a :term:`dotted Python name` + which refers to a view callable. This argument is required + unless a ``renderer`` argument also exists. If a + ``renderer`` argument is passed, and a ``view`` argument is + not provided, the view callable defaults to a callable that + returns an empty dictionary (see + :ref:`views_which_use_a_renderer`). + + permission + + The name of a :term:`permission` that the user must possess + in order to invoke the :term:`view callable`. See + :ref:`view_security_section` for more information about view + security and permissions. If ``permission`` is omitted, a + *default* permission may be used for this view registration + if one was named as the + :class:`pyramid.config.Configurator` constructor's + ``default_permission`` argument, or if + :meth:`pyramid.config.Configurator.set_default_permission` + was used prior to this view registration. Pass the string + ``__no_permission_required__`` as the permission argument to + explicitly indicate that the view should always be + executable by entirely anonymous users, regardless of the + default permission, bypassing any :term:`authorization + policy` that may be in effect. + + attr + + The view machinery defaults to using the ``__call__`` method + of the :term:`view callable` (or the function itself, if the + view callable is a function) to obtain a response. The + ``attr`` value allows you to vary the method attribute used + to obtain the response. For example, if your view was a + class, and the class has a method named ``index`` and you + wanted to use this method instead of the class' ``__call__`` + method to return the response, you'd say ``attr="index"`` in the + view configuration for the view. This is + most useful when the view definition is a class. + + renderer + + This is either a single string term (e.g. ``json``) or a + string implying a path or :term:`resource specification` + (e.g. ``templates/views.pt``) naming a :term:`renderer` + implementation. If the ``renderer`` value does not contain + a dot ``.``, the specified string will be used to look up a + renderer implementation, and that renderer implementation + will be used to construct a response from the view return + value. If the ``renderer`` value contains a dot (``.``), + the specified term will be treated as a path, and the + filename extension of the last element in the path will be + used to look up the renderer implementation, which will be + passed the full path. The renderer implementation will be + used to construct a :term:`response` from the view return + value. + + Note that if the view itself returns a :term:`response` (see + :ref:`the_response`), the specified renderer implementation + is never called. + + When the renderer is a path, although a path is usually just + a simple relative pathname (e.g. ``templates/foo.pt``, + implying that a template named "foo.pt" is in the + "templates" directory relative to the directory of the + current :term:`package` of the Configurator), a path can be + absolute, starting with a slash on UNIX or a drive letter + prefix on Windows. The path can alternately be a + :term:`resource specification` in the form + ``some.dotted.package_name:relative/path``, making it + possible to address template resources which live in a + separate package. + + The ``renderer`` attribute is optional. If it is not + defined, the "null" renderer is assumed (no rendering is + performed and the value is passed back to the upstream + :app:`Pyramid` machinery unmolested). + + wrapper + + The :term:`view name` of a different :term:`view + configuration` which will receive the response body of this + view as the ``request.wrapped_body`` attribute of its own + :term:`request`, and the :term:`response` returned by this + view as the ``request.wrapped_response`` attribute of its + own request. Using a wrapper makes it possible to "chain" + views together to form a composite response. The response + of the outermost wrapper view will be returned to the user. + The wrapper view will be found as any view is found: see + :ref:`view_lookup`. The "best" wrapper view will be found + based on the lookup ordering: "under the hood" this wrapper + view is looked up via + ``pyramid.view.render_view_to_response(context, request, + 'wrapper_viewname')``. The context and request of a wrapper + view is the same context and request of the inner view. If + this attribute is unspecified, no view wrapping is done. + + Predicate Arguments + + name + + The :term:`view name`. Read :ref:`traversal_chapter` to + understand the concept of a view name. + + context + + An object or a :term:`dotted Python name` referring to an + interface or class object that the :term:`context` must be + an instance of, *or* the :term:`interface` that the + :term:`context` must provide in order for this view to be + found and called. This predicate is true when the + :term:`context` is an instance of the represented class or + if the :term:`context` provides the represented interface; + it is otherwise false. This argument may also be provided + to ``add_view`` as ``for_`` (an older, still-supported + spelling). + + route_name + + This value must match the ``name`` of a :term:`route + configuration` declaration (see :ref:`urldispatch_chapter`) + that must match before this view will be called. Note that + the ``route`` configuration referred to by ``route_name`` + usually has a ``*traverse`` token in the value of its + ``path``, representing a part of the path that will be used + by :term:`traversal` against the result of the route's + :term:`root factory`. + + .. warning:: Using this argument services an advanced + feature that isn't often used unless you want to perform + traversal *after* a route has matched. See + :ref:`hybrid_chapter` for more information on using this + advanced feature. + + request_type + + This value should be an :term:`interface` that the + :term:`request` must provide in order for this view to be + found and called. This value exists only for backwards + compatibility purposes. + + request_method + + This value can either be one of the strings ``GET``, + ``POST``, ``PUT``, ``DELETE``, or ``HEAD`` representing an + HTTP ``REQUEST_METHOD``. A view declaration with this + argument ensures that the view will only be called when the + request's ``method`` attribute (aka the ``REQUEST_METHOD`` of + the WSGI environment) string matches the supplied value. + + request_param + + This value can be any string. A view declaration with this + argument ensures that the view will only be called when the + :term:`request` has a key in the ``request.params`` + dictionary (an HTTP ``GET`` or ``POST`` variable) that has a + name which matches the supplied value. If the value + supplied has a ``=`` sign in it, + e.g. ``request_params="foo=123"``, then the key (``foo``) + must both exist in the ``request.params`` dictionary, *and* + the value must match the right hand side of the expression + (``123``) for the view to "match" the current request. + + containment + + This value should be a Python class or :term:`interface` or + a :term:`dotted Python name` to such an object that a parent + object in the :term:`lineage` must provide in order for this + view to be found and called. The nodes in your object graph + must be "location-aware" to use this feature. See + :ref:`location_aware` for more information about + location-awareness. + + xhr + + This value should be either ``True`` or ``False``. If this + value is specified and is ``True``, the :term:`request` + must possess an ``HTTP_X_REQUESTED_WITH`` (aka + ``X-Requested-With``) header that has the value + ``XMLHttpRequest`` for this view to be found and called. + This is useful for detecting AJAX requests issued from + jQuery, Prototype and other Javascript libraries. + + accept + + The value of this argument represents a match query for one + or more mimetypes in the ``Accept`` HTTP request header. If + this value is specified, it must be in one of the following + forms: a mimetype match token in the form ``text/plain``, a + wildcard mimetype match token in the form ``text/*`` or a + match-all wildcard mimetype match token in the form ``*/*``. + If any of the forms matches the ``Accept`` header of the + request, this predicate will be true. + + header + + This value represents an HTTP header name or a header + name/value pair. If the value contains a ``:`` (colon), it + will be considered a name/value pair + (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). The + value portion should be a regular expression. If the value + does not contain a colon, the entire value will be + considered to be the header name + (e.g. ``If-Modified-Since``). If the value evaluates to a + header name only without a value, the header specified by + the name must be present in the request for this predicate + to be true. If the value evaluates to a header name/value + pair, the header specified by the name must be present in + the request *and* the regular expression specified as the + value must match the header value. Whether or not the value + represents a header name or a header name/value pair, the + case of the header name is not significant. + + path_info + + This value represents a regular expression pattern that will + be tested against the ``PATH_INFO`` WSGI environment + variable. If the regex matches, this predicate will be + ``True``. + + + custom_predicates + + This value should be a sequence of references to custom + predicate callables. Use custom predicates when no set of + predefined predicates do what you need. Custom predicates + can be combined with predefined predicates as necessary. + Each custom predicate callable should accept two arguments: + ``context`` and ``request`` and should return either + ``True`` or ``False`` after doing arbitrary evaluation of + the context and/or the request. If all callables return + ``True``, the associated view callable will be considered + viable for a given request. + + """ + view = self.maybe_dotted(view) + context = self.maybe_dotted(context) + for_ = self.maybe_dotted(for_) + containment = self.maybe_dotted(containment) + + if not view: + if renderer: + def view(context, request): + return {} + else: + raise ConfigurationError('"view" was not specified and ' + 'no "renderer" specified') + + if request_type is not None: + request_type = self.maybe_dotted(request_type) + if not IInterface.providedBy(request_type): + raise ConfigurationError( + 'request_type must be an interface, not %s' % request_type) + + request_iface = IRequest + + if route_name is not None: + request_iface = self.registry.queryUtility(IRouteRequest, + name=route_name) + if request_iface is None: + deferred_views = getattr(self.registry, + 'deferred_route_views', None) + if deferred_views is None: + deferred_views = self.registry.deferred_route_views = {} + info = dict( + view=view, name=name, for_=for_, permission=permission, + request_type=request_type, route_name=route_name, + request_method=request_method, request_param=request_param, + containment=containment, attr=attr, + renderer=renderer, wrapper=wrapper, xhr=xhr, accept=accept, + header=header, path_info=path_info, + custom_predicates=custom_predicates, context=context, + ) + view_info = deferred_views.setdefault(route_name, []) + view_info.append(info) + return + + order, predicates, phash = _make_predicates(xhr=xhr, + request_method=request_method, path_info=path_info, + request_param=request_param, header=header, accept=accept, + containment=containment, request_type=request_type, + custom=custom_predicates) + + if renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + + if context is None: + context = for_ + + r_context = context + if r_context is None: + r_context = Interface + if not IInterface.providedBy(r_context): + r_context = implementedBy(r_context) + + def register(permission=permission): + + if permission is None: + # intent: will be None if no default permission is registered + permission = self.registry.queryUtility(IDefaultPermission) + + # NO_PERMISSION_REQUIRED handled by _secure_view + derived_view = self._derive_view(view, permission, predicates, attr, + renderer, wrapper, name, accept, + order, phash) + + registered = self.registry.adapters.registered + + # A multiviews is a set of views which are registered for + # exactly the same context type/request type/name triad. Each + # consituent view in a multiview differs only by the + # predicates which it possesses. + + # To find a previously registered view for a context + # type/request type/name triad, we need to use the + # ``registered`` method of the adapter registry rather than + # ``lookup``. ``registered`` ignores interface inheritance + # for the required and provided arguments, returning only a + # view registered previously with the *exact* triad we pass + # in. + + # We need to do this three times, because we use three + # different interfaces as the ``provided`` interface while + # doing registrations, and ``registered`` performs exact + # matches on all the arguments it receives. + + old_view = None + + for view_type in (IView, ISecuredView, IMultiView): + old_view = registered((IViewClassifier, request_iface, + r_context), view_type, name) + if old_view is not None: + break + + isexc = isexception(context) + + def regclosure(): + if hasattr(derived_view, '__call_permissive__'): + view_iface = ISecuredView + else: + view_iface = IView + self.registry.registerAdapter( + derived_view, + (IViewClassifier, request_iface, context), view_iface, name + ) + if isexc: + self.registry.registerAdapter( + derived_view, + (IExceptionViewClassifier, request_iface, context), + view_iface, name) + + is_multiview = IMultiView.providedBy(old_view) + old_phash = getattr(old_view, '__phash__', DEFAULT_PHASH) + + if old_view is None: + # - No component was yet registered for any of our I*View + # interfaces exactly; this is the first view for this + # triad. + regclosure() + + elif (not is_multiview) and (old_phash == phash): + # - A single view component was previously registered with + # the same predicate hash as this view; this registration + # is therefore an override. + regclosure() + + else: + # - A view or multiview was already registered for this + # triad, and the new view is not an override. + + # XXX we could try to be more efficient here and register + # a non-secured view for a multiview if none of the + # multiview's consituent views have a permission + # associated with them, but this code is getting pretty + # rough already + if is_multiview: + multiview = old_view + else: + multiview = MultiView(name) + old_accept = getattr(old_view, '__accept__', None) + old_order = getattr(old_view, '__order__', MAX_ORDER) + multiview.add(old_view, old_order, old_accept, old_phash) + multiview.add(derived_view, order, accept, phash) + for view_type in (IView, ISecuredView): + # unregister any existing views + self.registry.adapters.unregister( + (IViewClassifier, request_iface, r_context), + view_type, name=name) + if isexc: + self.registry.adapters.unregister( + (IExceptionViewClassifier, request_iface, + r_context), view_type, name=name) + self.registry.registerAdapter( + multiview, + (IViewClassifier, request_iface, context), + IMultiView, name=name) + if isexc: + self.registry.registerAdapter( + multiview, + (IExceptionViewClassifier, request_iface, context), + IMultiView, name=name) + + discriminator = [ + 'view', context, name, request_type, IView, containment, + request_param, request_method, route_name, attr, + xhr, accept, header, path_info] + discriminator.extend(sorted(custom_predicates)) + discriminator = tuple(discriminator) + self.action(discriminator, register) + + #@action_method + def add_route(self, + name, + pattern=None, + view=None, + view_for=None, + permission=None, + factory=None, + for_=None, + header=None, + xhr=False, + accept=None, + path_info=None, + request_method=None, + request_param=None, + traverse=None, + custom_predicates=(), + view_permission=None, + renderer=None, + view_renderer=None, + view_context=None, + view_attr=None, + use_global_views=False, + path=None, + pregenerator=None, + ): + """ Add a :term:`route configuration` to the current + configuration state, as well as possibly a :term:`view + configuration` to be used to specify a :term:`view callable` + that will be invoked when this route matches. The arguments + to this method are divided into *predicate*, *non-predicate*, + and *view-related* types. :term:`Route predicate` arguments + narrow the circumstances in which a route will be match a + request; non-predicate arguments are informational. + + Non-Predicate Arguments + + name + + The name of the route, e.g. ``myroute``. This attribute is + required. It must be unique among all defined routes in a given + application. + + factory + + A Python object (often a function or a class) or a + :term:`dotted Python name` which refers to the same object + that will generate a :app:`Pyramid` :term:`context` + object when this route matches. For example, + ``mypackage.models.MyFactoryClass``. If this argument is + not specified, a default root factory will be used. + + traverse + + If you would like to cause the :term:`context` to be + something other than the :term:`root` object when this route + matches, you can spell a traversal pattern as the + ``traverse`` argument. This traversal pattern will be used + as the traversal path: traversal will begin at the root + object implied by this route (either the global root, or the + object returned by the ``factory`` associated with this + route). + + The syntax of the ``traverse`` argument is the same as it is + for ``pattern``. For example, if the ``pattern`` provided to + ``add_route`` is ``articles/{article}/edit``, and the + ``traverse`` argument provided to ``add_route`` is + ``/{article}``, when a request comes in that causes the route + to match in such a way that the ``article`` match value is + '1' (when the request URI is ``/articles/1/edit``), the + traversal path will be generated as ``/1``. This means that + the root object's ``__getitem__`` will be called with the + name ``1`` during the traversal phase. If the ``1`` object + exists, it will become the :term:`context` of the request. + :ref:`traversal_chapter` has more information about + traversal. + + If the traversal path contains segment marker names which + are not present in the ``pattern`` argument, a runtime error + will occur. The ``traverse`` pattern should not contain + segment markers that do not exist in the ``pattern`` + argument. + + A similar combining of routing and traversal is available + when a route is matched which contains a ``*traverse`` + remainder marker in its pattern (see + :ref:`using_traverse_in_a_route_pattern`). The ``traverse`` + argument to add_route allows you to associate route patterns + with an arbitrary traversal path without using a a + ``*traverse`` remainder marker; instead you can use other + match information. + + Note that the ``traverse`` argument to ``add_route`` is + ignored when attached to a route that has a ``*traverse`` + remainder marker in its pattern. + + pregenerator + + This option should be a callable object that implements the + :class:`pyramid.interfaces.IRoutePregenerator` + interface. A :term:`pregenerator` is a callable called by + the :mod:`pyramid.url.route_url` function to augment or + replace the arguments it is passed when generating a URL + for the route. This is a feature not often used directly + by applications, it is meant to be hooked by frameworks + that use :app:`Pyramid` as a base. + + Predicate Arguments + + pattern + + The pattern of the route e.g. ``ideas/{idea}``. This + argument is required. See :ref:`route_path_pattern_syntax` + for information about the syntax of route patterns. If the + pattern doesn't match the current URL, route matching + continues. + + .. note:: For backwards compatibility purposes (as of + :app:`Pyramid` 1.0), a ``path`` keyword argument passed + to this function will be used to represent the pattern + value if the ``pattern`` argument is ``None``. If both + ``path`` and ``pattern`` are passed, ``pattern`` wins. + + xhr + + This value should be either ``True`` or ``False``. If this + value is specified and is ``True``, the :term:`request` must + possess an ``HTTP_X_REQUESTED_WITH`` (aka + ``X-Requested-With``) header for this route to match. This + is useful for detecting AJAX requests issued from jQuery, + Prototype and other Javascript libraries. If this predicate + returns ``False``, route matching continues. + + request_method + + A string representing an HTTP method name, e.g. ``GET``, + ``POST``, ``HEAD``, ``DELETE``, ``PUT``. If this argument + is not specified, this route will match if the request has + *any* request method. If this predicate returns ``False``, + route matching continues. + + path_info + + This value represents a regular expression pattern that will + be tested against the ``PATH_INFO`` WSGI environment + variable. If the regex matches, this predicate will return + ``True``. If this predicate returns ``False``, route + matching continues. + + request_param + + This value can be any string. A view declaration with this + argument ensures that the associated route will only match + when the request has a key in the ``request.params`` + dictionary (an HTTP ``GET`` or ``POST`` variable) that has a + name which matches the supplied value. If the value + supplied as the argument has a ``=`` sign in it, + e.g. ``request_params="foo=123"``, then the key + (``foo``) must both exist in the ``request.params`` dictionary, and + the value must match the right hand side of the expression (``123``) + for the route to "match" the current request. If this predicate + returns ``False``, route matching continues. + + header + + This argument represents an HTTP header name or a header + name/value pair. If the argument contains a ``:`` (colon), + it will be considered a name/value pair + (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). If + the value contains a colon, the value portion should be a + regular expression. If the value does not contain a colon, + the entire value will be considered to be the header name + (e.g. ``If-Modified-Since``). If the value evaluates to a + header name only without a value, the header specified by + the name must be present in the request for this predicate + to be true. If the value evaluates to a header name/value + pair, the header specified by the name must be present in + the request *and* the regular expression specified as the + value must match the header value. Whether or not the value + represents a header name or a header name/value pair, the + case of the header name is not significant. If this + predicate returns ``False``, route matching continues. + + accept + + This value represents a match query for one or more + mimetypes in the ``Accept`` HTTP request header. If this + value is specified, it must be in one of the following + forms: a mimetype match token in the form ``text/plain``, a + wildcard mimetype match token in the form ``text/*`` or a + match-all wildcard mimetype match token in the form ``*/*``. + If any of the forms matches the ``Accept`` header of the + request, this predicate will be true. If this predicate + returns ``False``, route matching continues. + + custom_predicates + + This value should be a sequence of references to custom + predicate callables. Use custom predicates when no set of + predefined predicates does what you need. Custom predicates + can be combined with predefined predicates as necessary. + Each custom predicate callable should accept two arguments: + ``info`` and ``request`` and should return either ``True`` + or ``False`` after doing arbitrary evaluation of the info + and/or the request. If all custom and non-custom predicate + callables return ``True`` the associated route will be + considered viable for a given request. If any predicate + callable returns ``False``, route matching continues. Note + that the value ``info`` passed to a custom route predicate + is a dictionary containing matching information; see + :ref:`custom_route_predicates` for more information about + ``info``. + + View-Related Arguments + + view + + A Python object or :term:`dotted Python name` to the same + object that will be used as a view callable when this route + matches. e.g. ``mypackage.views.my_view``. + + view_context + + A class or an :term:`interface` or :term:`dotted Python + name` to the same object which the :term:`context` of the + view should match for the view named by the route to be + used. This argument is only useful if the ``view`` + attribute is used. If this attribute is not specified, the + default (``None``) will be used. + + If the ``view`` argument is not provided, this argument has + no effect. + + This attribute can also be spelled as ``for_`` or ``view_for``. + + view_permission + + The permission name required to invoke the view associated + with this route. e.g. ``edit``. (see + :ref:`using_security_with_urldispatch` for more information + about permissions). + + If the ``view`` attribute is not provided, this argument has + no effect. + + This argument can also be spelled as ``permission``. + + view_renderer + + This is either a single string term (e.g. ``json``) or a + string implying a path or :term:`resource specification` + (e.g. ``templates/views.pt``). If the renderer value is a + single term (does not contain a dot ``.``), the specified + term will be used to look up a renderer implementation, and + that renderer implementation will be used to construct a + response from the view return value. If the renderer term + contains a dot (``.``), the specified term will be treated + as a path, and the filename extension of the last element in + the path will be used to look up the renderer + implementation, which will be passed the full path. The + renderer implementation will be used to construct a response + from the view return value. See + :ref:`views_which_use_a_renderer` for more information. + + If the ``view`` argument is not provided, this argument has + no effect. + + This argument can also be spelled as ``renderer``. + + view_attr + + The view machinery defaults to using the ``__call__`` method + of the view callable (or the function itself, if the view + callable is a function) to obtain a response dictionary. + The ``attr`` value allows you to vary the method attribute + used to obtain the response. For example, if your view was + a class, and the class has a method named ``index`` and you + wanted to use this method instead of the class' ``__call__`` + method to return the response, you'd say ``attr="index"`` in + the view configuration for the view. This is + most useful when the view definition is a class. + + 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 + ignored, predicates, ignored = _make_predicates( + xhr=xhr, + request_method=request_method, + path_info=path_info, + request_param=request_param, + header=header, + accept=accept, + traverse=traverse, + custom=custom_predicates + ) + + request_iface = self.registry.queryUtility(IRouteRequest, name=name) + if request_iface is None: + bases = use_global_views and (IRequest,) or () + request_iface = route_request_iface(name, bases) + self.registry.registerUtility( + request_iface, IRouteRequest, name=name) + deferred_views = getattr(self.registry, 'deferred_route_views', {}) + view_info = deferred_views.pop(name, ()) + for info in view_info: + self.add_view(**info) + + if view: + if view_context is None: + view_context = view_for + if view_context is None: + view_context = for_ + view_permission = view_permission or permission + view_renderer = view_renderer or renderer + self.add_view( + permission=view_permission, + context=view_context, + view=view, + name='', + route_name=name, + renderer=view_renderer, + attr=view_attr, + ) + + mapper = self.get_routes_mapper() + + factory = self.maybe_dotted(factory) + if pattern is None: + pattern = path + if pattern is None: + raise ConfigurationError('"pattern" argument may not be None') + + discriminator = ['route', name, xhr, request_method, path_info, + request_param, header, accept] + discriminator.extend(sorted(custom_predicates)) + discriminator = tuple(discriminator) + + self.action(discriminator, None) + + return mapper.connect(name, pattern, factory, predicates=predicates, + pregenerator=pregenerator) + + def get_routes_mapper(self): + """ Return the :term:`routes mapper` object associated with + this configurator's :term:`registry`.""" + mapper = self.registry.queryUtility(IRoutesMapper) + if mapper is None: + mapper = RoutesMapper() + self.registry.registerUtility(mapper, IRoutesMapper) + return mapper + + #@action_method + def scan(self, package=None, categories=None): + """ Scan a Python package and any of its subpackages for + objects marked with :term:`configuration decoration` such as + :class:`pyramid.view.view_config`. Any decorated object found + will influence the current configuration state. + + The ``package`` argument should be a Python :term:`package` or + module object (or a :term:`dotted Python name` which refers to + such a package or module). If ``package`` is ``None``, the + package of the *caller* is used. + + The ``categories`` argument, if provided, should be the + :term:`Venusian` 'scan categories' to use during scanning. + Providing this argument is not often necessary; specifying + scan categories is an extremely advanced usage. + + By default, ``categories`` is ``None`` which will execute + *all* Venusian decorator callbacks including + :app:`Pyramid`-related decorators such as + :class:`pyramid.view.view_config`. If this is not desirable + because the codebase has other Venusian-using decorators that + aren't meant to be invoked during a particular scan, use + ``('pyramid',)`` as a ``categories`` value to limit the execution + of decorator callbacks to only those registered by + :app:`Pyramid` itself. Or pass a sequence of Venusian scan + categories as necessary (e.g. ``('pyramid', 'myframework')``) to + limit the decorators called to the set of categories required. + """ + package = self.maybe_dotted(package) + if package is None: # pragma: no cover + package = caller_package() + + scanner = self.venusian.Scanner(config=self) + scanner.scan(package, categories=categories) + + #@action_method + def add_renderer(self, name, factory): + """ + Add a :app:`Pyramid` :term:`renderer` factory to the + current configuration state. + + The ``name`` argument is the renderer name. Use ``None`` to + represent the default renderer (a renderer which will be used for all + views unless they name another renderer specifically). + + The ``factory`` argument is Python reference to an + implementation of a :term:`renderer` factory or a + :term:`dotted Python name` to same. + + Note that this function must be called *before* any + ``add_view`` invocation that names the renderer name as an + argument. As a result, it's usually a better idea to pass + globally used renderers into the ``Configurator`` constructor + in the sequence of renderers passed as ``renderer`` than it is + to use this method. + """ + factory = self.maybe_dotted(factory) + # if name is None or the empty string, we're trying to register + # a default renderer, but registerUtility is too dumb to accept None + # as a name + if not name: + name = '' + # we need to register renderers eagerly because they are used during + # view configuration + self.registry.registerUtility(factory, IRendererFactory, name=name) + self.action((IRendererFactory, name), None) + + #@action_method + def override_resource(self, to_override, override_with, _override=None): + """ Add a :app:`Pyramid` resource override to the current + configuration state. + + ``to_override`` is a :term:`resource specification` to the + resource being overridden. + + ``override_with`` is a :term:`resource specification` to the + resource that is performing the override. + + See :ref:`resources_chapter` for more + information about resource overrides.""" + if to_override == override_with: + raise ConfigurationError('You cannot override a resource with ' + 'itself') + + package = to_override + path = '' + if ':' in to_override: + package, path = to_override.split(':', 1) + + override_package = override_with + override_prefix = '' + 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)') + + 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)') + + override = _override or self._override # test jig + def register(): + __import__(package) + __import__(override_package) + from_package = sys.modules[package] + to_package = sys.modules[override_package] + override(from_package, path, to_package, override_prefix) + self.action(None, register) + + def set_forbidden_view(self, view=None, attr=None, renderer=None, + wrapper=None): + """ Add a default forbidden view to the current configuration + state. + + .. warning:: This method has been deprecated in :app:`Pyramid` + 1.0. *Do not use it for new development; it should only be + used to support older code bases which depend upon it.* See + :ref:`changing_the_forbidden_view` to see how a forbidden + view should be registered in new projects. + + The ``view`` argument should be a :term:`view callable` or a + :term:`dotted Python name` which refers to a view callable. + + The ``attr`` argument should be the attribute of the view + callable used to retrieve the response (see the ``add_view`` + method's ``attr`` argument for a description). + + The ``renderer`` argument should be the name of (or path to) a + :term:`renderer` used to generate a response for this view + (see the + :meth:`pyramid.config.Configurator.add_view` + method's ``renderer`` argument for information about how a + configurator relates to a renderer). + + The ``wrapper`` argument should be the name of another view + which will wrap this view when rendered (see the ``add_view`` + method's ``wrapper`` argument for a description).""" + if renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + view = self._derive_view(view, attr=attr, renderer=renderer) + def bwcompat_view(context, request): + context = getattr(request, 'context', None) + return view(context, request) + return self.add_view(bwcompat_view, context=Forbidden, wrapper=wrapper) + + def set_notfound_view(self, view=None, attr=None, renderer=None, + wrapper=None): + """ Add a default not found view to the current configuration + state. + + .. warning:: This method has been deprecated in + :app:`Pyramid` 1.0. *Do not use it for new development; + it should only be used to support older code bases which + depend upon it.* See :ref:`changing_the_notfound_view` to + see how a not found view should be registered in new + projects. + + The ``view`` argument should be a :term:`view callable` or a + :term:`dotted Python name` which refers to a view callable. + + The ``attr`` argument should be the attribute of the view + callable used to retrieve the response (see the ``add_view`` + method's ``attr`` argument for a description). + + The ``renderer`` argument should be the name of (or path to) a + :term:`renderer` used to generate a response for this view + (see the + :meth:`pyramid.config.Configurator.add_view` + method's ``renderer`` argument for information about how a + configurator relates to a renderer). + + The ``wrapper`` argument should be the name of another view + which will wrap this view when rendered (see the ``add_view`` + method's ``wrapper`` argument for a description). + """ + if renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + view = self._derive_view(view, attr=attr, renderer=renderer) + def bwcompat_view(context, request): + context = getattr(request, 'context', None) + return view(context, request) + return self.add_view(bwcompat_view, context=NotFound, wrapper=wrapper) + + #@action_method + def set_request_factory(self, factory): + """ The object passed as ``factory`` should be an object (or a + :term:`dotted Python name` which refers to an object) which + will be used by the :app:`Pyramid` router to create all + request objects. This factory object must have the same + methods and attributes as the + :class:`pyramid.request.Request` class (particularly + ``__call__``, and ``blank``). + + .. note:: Using the :meth:``request_factory`` argument to the + :class:`pyramid.config.Configurator` constructor + can be used to achieve the same purpose. + """ + factory = self.maybe_dotted(factory) + def register(): + self.registry.registerUtility(factory, IRequestFactory) + self.action(IRequestFactory, register) + + #@action_method + def set_renderer_globals_factory(self, factory): + """ The object passed as ``factory`` should be an callable (or + a :term:`dotted Python name` which refers to an callable) that + will be used by the :app:`Pyramid` rendering machinery as a + renderers global factory (see :ref:`adding_renderer_globals`). + + The ``factory`` callable must accept a single argument named + ``system`` (which will be a dictionary) and it must return a + dictionary. When an application uses a renderer, the + factory's return dictionary will be merged into the ``system`` + dictionary, and therefore will be made available to the code + which uses the renderer. + + .. note:: Using the :meth:`renderer_globals_factory` + argument to the + :class:`pyramid.config.Configurator` constructor + can be used to achieve the same purpose. + """ + factory = self.maybe_dotted(factory) + def register(): + self.registry.registerUtility(factory, IRendererGlobalsFactory) + self.action(IRendererGlobalsFactory, register) + + #@action_method + def set_locale_negotiator(self, negotiator): + """ + Set the :term:`locale negotiator` for this application. The + :term:`locale negotiator` is a callable which accepts a + :term:`request` object and which returns a :term:`locale + name`. The ``negotiator`` argument should be the locale + negotiator implementation or a :term:`dotted Python name` + which refers to such an implementation. + + Later calls to this method override earlier calls; there can + be only one locale negotiator active at a time within an + application. See :ref:`activating_translation` for more + information. + + .. note:: Using the ``locale_negotiator`` argument to the + :class:`pyramid.config.Configurator` constructor + can be used to achieve the same purpose. + """ + negotiator = self.maybe_dotted(negotiator) + def register(): + self.registry.registerUtility(negotiator, ILocaleNegotiator) + self.action(ILocaleNegotiator, register) + + #@action_method + def set_default_permission(self, permission): + """ + Set the default permission to be used by all subsequent + :term:`view configuration` registrations. ``permission`` + should be a :term:`permission` string to be used as the + default permission. An example of a permission + string:``'view'``. Adding a default permission makes it + unnecessary to protect each view configuration with an + explicit permission, unless your application policy requires + some exception for a particular view. + + If a default permission is *not* set, views represented by + view configuration registrations which do not explicitly + declare a permission will be executable by entirely anonymous + users (any authorization policy is ignored). + + Later calls to this method override earlier calls; there can + be only one default permission active at a time within an + application. + + See also :ref:`setting_a_default_permission`. + + .. note:: Using the ``default_permission`` argument to the + :class:`pyramid.config.Configurator` constructor + can be used to achieve the same purpose. + """ + # default permission used during view registration + self.registry.registerUtility(permission, IDefaultPermission) + self.action(IDefaultPermission, None) + + #@action_method + def set_session_factory(self, session_factory): + """ + Configure the application with a :term:`session factory`. If + this method is called, the ``session_factory`` argument must + be a session factory callable. + """ + def register(): + self.registry.registerUtility(session_factory, ISessionFactory) + self.action(ISessionFactory, register) + + #@action_method + def add_translation_dirs(self, *specs): + """ Add one or more :term:`translation directory` paths to the + current configuration state. The ``specs`` argument is a + sequence that may contain absolute directory paths + (e.g. ``/usr/share/locale``) or :term:`resource specification` + names naming a directory path (e.g. ``some.package:locale``) + or a combination of the two. + + Example: + + .. code-block:: python + + add_translations_dirs('/usr/share/locale', 'some.package:locale') + + """ + for spec in specs: + + package_name, filename = self._split_spec(spec) + if package_name is None: # absolute filename + directory = filename + else: + __import__(package_name) + package = sys.modules[package_name] + directory = os.path.join(package_path(package), filename) + + if not os.path.isdir(os.path.realpath(directory)): + raise ConfigurationError('"%s" is not a directory' % directory) + + tdirs = self.registry.queryUtility(ITranslationDirectories) + if tdirs is None: + tdirs = [] + self.registry.registerUtility(tdirs, ITranslationDirectories) + + tdirs.insert(0, directory) + # XXX no action? + + if specs: + + # We actually only need an IChameleonTranslate function + # utility to be registered zero or one times. We register the + # same function once for each added translation directory, + # which does too much work, but has the same effect. + + def translator(msg): + request = get_current_request() + localizer = get_localizer(request) + return localizer.translate(msg) + + ctranslate = ChameleonTranslate(translator) + self.registry.registerUtility(ctranslate, IChameleonTranslate) + + def add_static_view(self, name, path, **kw): + """ Add a view used to render static resources such as images + and CSS files. + + The ``name`` argument is a string representing :term:`view + name` of the view which is registered. It may alternately be + a *url prefix*. + + The ``path`` argument is the path on disk where the static + files reside. This can be an absolute path, a + package-relative path, or a :term:`resource specification`. + + The ``cache_max_age`` keyword argument is input to set the + ``Expires`` and ``Cache-Control`` headers for static resources + served. Note that this argument has no effect when the + ``name`` is a *url prefix*. By default, this argument is + ``None``, meaning that no particular Expires or Cache-Control + headers are set in the response. + + The ``permission`` keyword argument is used to specify the + :term:`permission` required by a user to execute the static + view. By default, it is the string + ``__no_permission_required__``. The + ``__no_permission_required__`` string is a special sentinel + which indicates that, even if a :term:`default permission` + exists for the current application, the static view should be + renderered to completely anonymous users. This default value + is permissive because, in most web apps, static resources + seldom need protection from viewing. + + *Usage* + + The ``add_static_view`` function is typically used in + conjunction with the :func:`pyramid.url.static_url` + function. ``add_static_view`` adds a view which renders a + static resource when some URL is visited; + :func:`pyramid.url.static_url` generates a URL to that + resource. + + The ``name`` argument to ``add_static_view`` is usually a + :term:`view name`. When this is the case, the + :func:`pyramid.url.static_url` API will generate a URL + which points to a Pyramid view, which will serve up a set of + resources that live in the package itself. For example: + + .. code-block:: python + + add_static_view('images', 'mypackage:images/') + + Code that registers such a view can generate URLs to the view + via :func:`pyramid.url.static_url`: + + .. code-block:: python + + static_url('mypackage:images/logo.png', request) + + When ``add_static_view`` is called with a ``name`` argument + that represents a simple view name, as it is above, subsequent + calls to :func:`pyramid.url.static_url` with paths that + start with the ``path`` argument passed to ``add_static_view`` + will generate a URL something like ``http://<Pyramid app + URL>/images/logo.png``, which will cause the ``logo.png`` file + in the ``images`` subdirectory of the ``mypackage`` package to + be served. + + ``add_static_view`` can alternately be used with a ``name`` + argument which is a *URL*, causing static resources to be + served from an external webserver. This happens when the + ``name`` argument is a URL (detected as any string with a + slash in it). In this mode, the ``name`` is used as the URL + prefix when generating a URL using + :func:`pyramid.url.static_url`. For example, if + ``add_static_view`` is called like so: + + .. code-block:: python + + add_static_view('http://example.com/images', 'mypackage:images/') + + Subsequently, the URLs generated by + :func:`pyramid.url.static_url` for that static view will be + prefixed with ``http://example.com/images``: + + .. code-block:: python + + static_url('mypackage:images/logo.png', request) + + When ``add_static_view`` is called with a ``name`` argument + that is the URL prefix ``http://example.com/images``, + subsequent calls to :func:`pyramid.url.static_url` with + paths that start with the ``path`` argument passed to + ``add_static_view`` will generate a URL something like + ``http://example.com/logo.png``. The external webserver + listening on ``example.com`` must be itself configured to + respond properly to such a request. + + See :ref:`static_resources_section` for more information. + """ + spec = self._make_spec(path) + info = self.registry.queryUtility(IStaticURLInfo) + if info is None: + info = StaticURLInfo(self) + self.registry.registerUtility(info, IStaticURLInfo) + + info.add(name, spec, **kw) + + # testing API + def testing_securitypolicy(self, userid=None, groupids=(), + permissive=True): + """Unit/integration testing helper: Registers a pair of faux + :app:`Pyramid` security policies: a :term:`authentication + policy` and a :term:`authorization policy`. + + The behavior of the registered :term:`authorization policy` + depends on the ``permissive`` argument. If ``permissive`` is + true, a permissive :term:`authorization policy` is registered; + this policy allows all access. If ``permissive`` is false, a + nonpermissive :term:`authorization policy` is registered; this + policy denies all access. + + The behavior of the registered :term:`authentication policy` + depends on the values provided for the ``userid`` and + ``groupids`` argument. The authentication policy will return + the userid identifier implied by the ``userid`` argument and + the group ids implied by the ``groupids`` argument when the + :func:`pyramid.security.authenticated_userid` or + :func:`pyramid.security.effective_principals` APIs are + used. + + This function is most useful when testing code that uses + the APIs named :func:`pyramid.security.has_permission`, + :func:`pyramid.security.authenticated_userid`, + :func:`pyramid.security.effective_principals`, and + :func:`pyramid.security.principals_allowed_by_permission`. + """ + from pyramid.testing import DummySecurityPolicy + policy = DummySecurityPolicy(userid, groupids, permissive) + self.registry.registerUtility(policy, IAuthorizationPolicy) + self.registry.registerUtility(policy, IAuthenticationPolicy) + + def testing_models(self, models): + """Unit/integration testing helper: registers a dictionary of + :term:`model` objects that can be resolved via the + :func:`pyramid.traversal.find_model` API. + + The :func:`pyramid.traversal.find_model` API is called with + a path as one of its arguments. If the dictionary you + register when calling this method contains that path as a + string key (e.g. ``/foo/bar`` or ``foo/bar``), the + corresponding value will be returned to ``find_model`` (and + thus to your code) when + :func:`pyramid.traversal.find_model` is called with an + equivalent path string or tuple. + """ + class DummyTraverserFactory: + def __init__(self, context): + self.context = context + + def __call__(self, request): + path = request['PATH_INFO'] + ob = models[path] + traversed = traversal_path(path) + return {'context':ob, 'view_name':'','subpath':(), + 'traversed':traversed, 'virtual_root':ob, + 'virtual_root_path':(), 'root':ob} + self.registry.registerAdapter(DummyTraverserFactory, (Interface,), + ITraverser) + return models + + def testing_add_subscriber(self, event_iface=None): + """Unit/integration testing helper: Registers a + :term:`subscriber` which listens for events of the type + ``event_iface``. This method returns a list object which is + appended to by the subscriber whenever an event is captured. + + When an event is dispatched that matches the value implied by + the ``event_iface`` argument, that event will be appended to + the list. You can then compare the values in the list to + expected event notifications. This method is useful when + testing code that wants to call + :meth:`pyramid.registry.Registry.notify`, + :func:`zope.component.event.dispatch` or + :func:`zope.component.event.objectEventNotify`. + + The default value of ``event_iface`` (``None``) implies a + subscriber registered for *any* kind of event. + """ + event_iface = self.maybe_dotted(event_iface) + L = [] + def subscriber(*event): + L.extend(event) + self.add_subscriber(subscriber, event_iface) + return L + + def testing_add_renderer(self, path, renderer=None): + """Unit/integration testing helper: register a renderer at + ``path`` (usually a relative filename ala ``templates/foo.pt`` + or a resource specification) and return the renderer object. + If the ``renderer`` argument is None, a 'dummy' renderer will + be used. This function is useful when testing code that calls + the :func:`pyramid.renderers.render` function or + :func:`pyramid.renderers.render_to_response` function or + any other ``render_*`` or ``get_*`` API of the + :mod:`pyramid.renderers` module. + + Note that calling this method for with a ``path`` argument + representing a renderer factory type (e.g. for ``foo.pt`` + usually implies the ``chameleon_zpt`` renderer factory) + clobbers any existing renderer factory registered for that + type. + + .. note:: This method is also available under the alias + ``testing_add_template`` (an older name for it). + + """ + from pyramid.testing import DummyRendererFactory + helper = RendererHelper(name=path, registry=self.registry) + factory = self.registry.queryUtility(IRendererFactory, name=helper.type) + if not isinstance(factory, DummyRendererFactory): + factory = DummyRendererFactory(helper.type, factory) + self.registry.registerUtility(factory, IRendererFactory, + name=helper.type) + + from pyramid.testing import DummyTemplateRenderer + if renderer is None: + renderer = DummyTemplateRenderer() + factory.add(path, renderer) + return renderer + + testing_add_template = testing_add_renderer + +def _make_predicates(xhr=None, request_method=None, path_info=None, + request_param=None, header=None, accept=None, + containment=None, request_type=None, + traverse=None, custom=()): + + # PREDICATES + # ---------- + # + # Given an argument list, a predicate list is computed. + # Predicates are added to a predicate list in (presumed) + # computation expense order. All predicates associated with a + # view or route must evaluate true for the view or route to + # "match" during a request. Elsewhere in the code, we evaluate + # predicates using a generator expression. The fastest predicate + # should be evaluated first, then the next fastest, and so on, as + # if one returns false, the remainder of the predicates won't need + # to be evaluated. + # + # While we compute predicates, we also compute a predicate hash + # (aka phash) that can be used by a caller to identify identical + # predicate lists. + # + # ORDERING + # -------- + # + # A "order" is computed for the predicate list. An order is + # a scoring. + # + # Each predicate is associated with a weight value, which is a + # multiple of 2. The weight of a predicate symbolizes the + # relative potential "importance" of the predicate to all other + # predicates. A larger weight indicates greater importance. + # + # All weights for a given predicate list are bitwise ORed together + # to create a "score"; this score is then subtracted from + # MAX_ORDER and divided by an integer representing the number of + # predicates+1 to determine the order. + # + # The order represents the ordering in which a "multiview" ( a + # collection of views that share the same context/request/name + # triad but differ in other ways via predicates) will attempt to + # call its set of views. Views with lower orders will be tried + # first. The intent is to a) ensure that views with more + # predicates are always evaluated before views with fewer + # predicates and b) to ensure a stable call ordering of views that + # share the same number of predicates. Views which do not have + # any predicates get an order of MAX_ORDER, meaning that they will + # be tried very last. + + predicates = [] + weights = [] + h = md5() + + if xhr: + def xhr_predicate(context, request): + return request.is_xhr + weights.append(1 << 1) + predicates.append(xhr_predicate) + h.update('xhr:%r' % bool(xhr)) + + if request_method is not None: + def request_method_predicate(context, request): + return request.method == request_method + weights.append(1 << 2) + predicates.append(request_method_predicate) + h.update('request_method:%r' % request_method) + + if path_info is not None: + try: + path_info_val = re.compile(path_info) + except re.error, why: + raise ConfigurationError(why[0]) + def path_info_predicate(context, request): + return path_info_val.match(request.path_info) is not None + weights.append(1 << 3) + predicates.append(path_info_predicate) + h.update('path_info:%r' % path_info) + + if request_param is not None: + request_param_val = None + if '=' in request_param: + request_param, request_param_val = request_param.split('=', 1) + def request_param_predicate(context, request): + if request_param_val is None: + return request_param in request.params + return request.params.get(request_param) == request_param_val + weights.append(1 << 4) + predicates.append(request_param_predicate) + h.update('request_param:%r=%r' % (request_param, request_param_val)) + + if header is not None: + header_name = header + header_val = None + if ':' in header: + header_name, header_val = header.split(':', 1) + try: + header_val = re.compile(header_val) + except re.error, why: + raise ConfigurationError(why[0]) + def header_predicate(context, request): + if header_val is None: + return header_name in request.headers + val = request.headers.get(header_name) + if val is None: + return False + return header_val.match(val) is not None + weights.append(1 << 5) + predicates.append(header_predicate) + h.update('header:%r=%r' % (header_name, header_val)) + + if accept is not None: + def accept_predicate(context, request): + return accept in request.accept + weights.append(1 << 6) + predicates.append(accept_predicate) + h.update('accept:%r' % accept) + + if containment is not None: + def containment_predicate(context, request): + return find_interface(context, containment) is not None + weights.append(1 << 7) + predicates.append(containment_predicate) + h.update('containment:%r' % hash(containment)) + + if request_type is not None: + def request_type_predicate(context, request): + return request_type.providedBy(request) + weights.append(1 << 8) + predicates.append(request_type_predicate) + h.update('request_type:%r' % hash(request_type)) + + if traverse is not None: + # ``traverse`` can only be used as a *route* "predicate"; it + # adds 'traverse' to the matchdict if it's specified in the + # routing args. This causes the ModelGraphTraverser to use + # the resolved traverse pattern as the traversal path. + from pyramid.urldispatch import _compile_route + _, tgenerate = _compile_route(traverse) + def traverse_predicate(context, request): + if 'traverse' in context: + return True + m = context['match'] + tvalue = tgenerate(m) + m['traverse'] = traversal_path(tvalue) + return True + # This isn't actually a predicate, it's just a infodict + # modifier that injects ``traverse`` into the matchdict. As a + # result, the ``traverse_predicate`` function above always + # returns True, and we don't need to update the hash or attach + # a weight to it + predicates.append(traverse_predicate) + + if custom: + for num, predicate in enumerate(custom): + predicates.append(predicate) + # using hash() here rather than id() is intentional: we + # want to allow custom predicates that are part of + # frameworks to be able to define custom __hash__ + # functions for custom predicates, so that the hash output + # of predicate instances which are "logically the same" + # may compare equal. + h.update('custom%s:%r' % (num, hash(predicate))) + weights.append(1 << 10) + + score = 0 + for bit in weights: + score = score | bit + order = (MAX_ORDER - score) / (len(predicates) + 1) + phash = h.hexdigest() + return order, predicates, phash + +class MultiView(object): + implements(IMultiView) + + def __init__(self, name): + self.name = name + self.media_views = {} + self.views = [] + self.accepts = [] + + def add(self, view, order, accept=None, phash=None): + if phash is not None: + for i, (s, v, h) in enumerate(list(self.views)): + if phash == h: + self.views[i] = (order, view, phash) + return + + if accept is None or '*' in accept: + self.views.append((order, view, phash)) + self.views.sort() + else: + subset = self.media_views.setdefault(accept, []) + subset.append((order, view, phash)) + subset.sort() + accepts = set(self.accepts) + accepts.add(accept) + self.accepts = list(accepts) # dedupe + + def get_views(self, request): + if self.accepts and hasattr(request, 'accept'): + accepts = self.accepts[:] + views = [] + while accepts: + match = request.accept.best_match(accepts) + if match is None: + break + subset = self.media_views[match] + views.extend(subset) + accepts.remove(match) + views.extend(self.views) + return views + return self.views + + def match(self, context, request): + for order, view, phash in self.get_views(request): + if not hasattr(view, '__predicated__'): + return view + if view.__predicated__(context, request): + return view + raise PredicateMismatch(self.name) + + def __permitted__(self, context, request): + view = self.match(context, request) + if hasattr(view, '__permitted__'): + return view.__permitted__(context, request) + return True + + def __call_permissive__(self, context, request): + view = self.match(context, request) + view = getattr(view, '__call_permissive__', view) + return view(context, request) + + def __call__(self, context, request): + for order, view, phash in self.get_views(request): + try: + return view(context, request) + except PredicateMismatch: + continue + raise PredicateMismatch(self.name) + +def decorate_view(wrapped_view, original_view): + if wrapped_view is original_view: + return False + wrapped_view.__module__ = original_view.__module__ + wrapped_view.__doc__ = original_view.__doc__ + try: + wrapped_view.__name__ = original_view.__name__ + except AttributeError: + wrapped_view.__name__ = repr(original_view) + try: + wrapped_view.__permitted__ = original_view.__permitted__ + except AttributeError: + pass + try: + wrapped_view.__call_permissive__ = original_view.__call_permissive__ + except AttributeError: + pass + try: + wrapped_view.__predicated__ = original_view.__predicated__ + except AttributeError: + pass + try: + wrapped_view.__accept__ = original_view.__accept__ + except AttributeError: + pass + try: + wrapped_view.__order__ = original_view.__order__ + except AttributeError: + pass + return True + +def requestonly(class_or_callable, attr=None): + """ Return true of the class or callable accepts only a request argument, + as opposed to something that accepts context, request """ + if attr is None: + attr = '__call__' + if inspect.isfunction(class_or_callable): + fn = class_or_callable + elif inspect.isclass(class_or_callable): + try: + fn = class_or_callable.__init__ + except AttributeError: + return False + else: + try: + fn = getattr(class_or_callable, attr) + except AttributeError: + return False + + try: + argspec = inspect.getargspec(fn) + except TypeError: + return False + + args = argspec[0] + defaults = argspec[3] + + if hasattr(fn, 'im_func'): + # it's an instance method + if not args: + return False + args = args[1:] + if not args: + return False + + if len(args) == 1: + return True + + elif args[0] == 'request': + if len(args) - len(defaults) == 1: + return True + + return False + +def is_response(ob): + if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and + hasattr(ob, 'status') ): + return True + return False + +def _map_view(view, registry, attr=None, renderer=None): + wrapped_view = view + + helper = None + + if renderer is not None: + helper = RendererHelper(renderer['name'], + package=renderer['package'], + registry=registry) + + if inspect.isclass(view): + # If the object we've located is a class, turn it into a + # function that operates like a Zope view (when it's invoked, + # construct an instance using 'context' and 'request' as + # position arguments, then immediately invoke the __call__ + # method of the instance with no arguments; __call__ should + # return an IResponse). + if requestonly(view, attr): + # its __init__ accepts only a single request argument, + # instead of both context and request + def _class_requestonly_view(context, request): + inst = view(request) + if attr is None: + response = inst() + else: + response = getattr(inst, attr)() + if helper is not None: + if not is_response(response): + system = { + 'view':inst, + 'renderer_name':renderer['name'], # b/c + 'renderer_info':renderer, + 'context':context, + 'request':request + } + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _class_requestonly_view + else: + # its __init__ accepts both context and request + def _class_view(context, request): + inst = view(context, request) + if attr is None: + response = inst() + else: + response = getattr(inst, attr)() + if helper is not None: + if not is_response(response): + system = {'view':inst, + 'renderer_name':renderer['name'], # b/c + 'renderer_info':renderer, + 'context':context, + 'request':request + } + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _class_view + + elif requestonly(view, attr): + # its __call__ accepts only a single request argument, + # instead of both context and request + def _requestonly_view(context, request): + if attr is None: + response = view(request) + else: + response = getattr(view, attr)(request) + + if helper is not None: + if not is_response(response): + system = { + 'view':view, + 'renderer_name':renderer['name'], + 'renderer_info':renderer, + 'context':context, + 'request':request + } + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _requestonly_view + + elif attr: + def _attr_view(context, request): + response = getattr(view, attr)(context, request) + if helper is not None: + if not is_response(response): + system = { + 'view':view, + 'renderer_name':renderer['name'], + 'renderer_info':renderer, + 'context':context, + 'request':request + } + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _attr_view + + elif helper is not None: + def _rendered_view(context, request): + response = view(context, request) + if not is_response(response): + system = { + 'view':view, + 'renderer_name':renderer['name'], # b/c + 'renderer_info':renderer, + 'context':context, + 'request':request + } + response = helper.render_to_response(response, system, + request=request) + return response + wrapped_view = _rendered_view + + decorate_view(wrapped_view, view) + return wrapped_view + +def _owrap_view(view, viewname, wrapper_viewname): + if not wrapper_viewname: + return view + def _owrapped_view(context, request): + response = view(context, request) + request.wrapped_response = response + request.wrapped_body = response.body + request.wrapped_view = view + wrapped_response = render_view_to_response(context, request, + wrapper_viewname) + if wrapped_response is None: + raise ValueError( + 'No wrapper view named %r found when executing view ' + 'named %r' % (wrapper_viewname, viewname)) + return wrapped_response + decorate_view(_owrapped_view, view) + return _owrapped_view + +def _predicate_wrap(view, predicates): + if not predicates: + return view + def predicate_wrapper(context, request): + if all((predicate(context, request) for predicate in predicates)): + return view(context, request) + raise PredicateMismatch('predicate mismatch for view %s' % view) + def checker(context, request): + return all((predicate(context, request) for predicate in + predicates)) + predicate_wrapper.__predicated__ = checker + decorate_view(predicate_wrapper, view) + return predicate_wrapper + +def _secure_view(view, permission, authn_policy, authz_policy): + if permission == '__no_permission_required__': + # allow views registered within configurations that have a + # default permission to explicitly override the default + # permission, replacing it with no permission at all + permission = None + + wrapped_view = view + if authn_policy and authz_policy and (permission is not None): + def _secured_view(context, request): + principals = authn_policy.effective_principals(request) + if authz_policy.permits(context, principals, permission): + return view(context, request) + msg = getattr(request, 'authdebug_message', + 'Unauthorized: %s failed permission check' % view) + raise Forbidden(msg) + _secured_view.__call_permissive__ = view + def _permitted(context, request): + principals = authn_policy.effective_principals(request) + return authz_policy.permits(context, principals, permission) + _secured_view.__permitted__ = _permitted + wrapped_view = _secured_view + decorate_view(wrapped_view, view) + + return wrapped_view + +def _authdebug_view(view, permission, authn_policy, authz_policy, settings, + logger): + wrapped_view = view + if settings and settings.get('debug_authorization', False): + def _authdebug_view(context, request): + view_name = getattr(request, 'view_name', None) + + if authn_policy and authz_policy: + if permission is None: + msg = 'Allowed (no permission registered)' + else: + principals = authn_policy.effective_principals(request) + msg = str(authz_policy.permits(context, principals, + permission)) + else: + msg = 'Allowed (no authorization policy in use)' + + view_name = getattr(request, 'view_name', None) + url = getattr(request, 'url', None) + msg = ('debug_authorization of url %s (view name %r against ' + 'context %r): %s' % (url, view_name, context, msg)) + logger and logger.debug(msg) + if request is not None: + request.authdebug_message = msg + return view(context, request) + + wrapped_view = _authdebug_view + decorate_view(wrapped_view, view) + + return wrapped_view + +def _attr_wrap(view, accept, order, phash): + # this is a little silly but we don't want to decorate the original + # function with attributes that indicate accept, order, and phash, + # so we use a wrapper + if (accept is None) and (order == MAX_ORDER) and (phash == DEFAULT_PHASH): + return view # defaults + def attr_view(context, request): + return view(context, request) + attr_view.__accept__ = accept + attr_view.__order__ = order + attr_view.__phash__ = phash + decorate_view(attr_view, view) + return attr_view + +def isexception(o): + if IInterface.providedBy(o): + if IException.isEqualOrExtendedBy(o): + return True + return ( + isinstance(o, Exception) or + (inspect.isclass(o) and (issubclass(o, Exception))) + ) + +class ActionPredicate(object): + action_name = 'action' + def __init__(self, action): + self.action = action + try: + self.action_re = re.compile(action + '$') + except (re.error, TypeError), why: + raise ConfigurationError(why[0]) + + def __call__(self, context, request): + matchdict = request.matchdict + if matchdict is None: + return False + action = matchdict.get(self.action_name) + if action is None: + return False + return bool(self.action_re.match(action)) + + def __hash__(self): + # allow this predicate's phash to be compared as equal to + # others that share the same action name + return hash(self.action) + +class PyramidConfigurationMachine(ConfigurationMachine): + autocommit = False + + def processSpec(self, spec): + """Check whether a callable needs to be processed. The ``spec`` + refers to a unique identifier for the callable. + + Return True if processing is needed and False otherwise. If + the callable needs to be processed, it will be marked as + processed, assuming that the caller will procces the callable if + it needs to be processed. + """ + if spec in self._seen_files: + return False + self._seen_files.add(spec) + return True + diff --git a/pyramid/configuration.py b/pyramid/configuration.py index b85c81cee..cdb9bc983 100644 --- a/pyramid/configuration.py +++ b/pyramid/configuration.py @@ -1,218 +1,18 @@ -import os -import re -import sys -import threading -import inspect - -import venusian - -from translationstring import ChameleonTranslate - -from zope.configuration import xmlconfig -from zope.configuration.config import ConfigurationMachine -from zope.configuration.xmlconfig import registerCommonDirectives - -from zope.interface import Interface -from zope.interface import implementedBy -from zope.interface.interfaces import IInterface -from zope.interface import implements - -from pyramid.interfaces import IAuthenticationPolicy -from pyramid.interfaces import IAuthorizationPolicy -from pyramid.interfaces import IChameleonTranslate -from pyramid.interfaces import IDebugLogger -from pyramid.interfaces import IDefaultPermission -from pyramid.interfaces import IDefaultRootFactory -from pyramid.interfaces import IException -from pyramid.interfaces import IExceptionResponse -from pyramid.interfaces import IExceptionViewClassifier -from pyramid.interfaces import ILocaleNegotiator -from pyramid.interfaces import IMultiView -from pyramid.interfaces import IPackageOverrides -from pyramid.interfaces import IRendererFactory -from pyramid.interfaces import IRendererGlobalsFactory -from pyramid.interfaces import IRequest -from pyramid.interfaces import IRequestFactory -from pyramid.interfaces import IRootFactory -from pyramid.interfaces import IRouteRequest -from pyramid.interfaces import IRoutesMapper -from pyramid.interfaces import ISecuredView -from pyramid.interfaces import ISessionFactory -from pyramid.interfaces import IStaticURLInfo -from pyramid.interfaces import ITranslationDirectories -from pyramid.interfaces import ITraverser -from pyramid.interfaces import IView -from pyramid.interfaces import IViewClassifier - -try: - from pyramid import chameleon_text -except TypeError: # pragma: no cover - chameleon_text = None # pypy -try: - from pyramid import chameleon_zpt -except TypeError: # pragma: no cover - chameleon_zpt = None # pypy - -from pyramid import renderers -from pyramid.authorization import ACLAuthorizationPolicy -from pyramid.compat import all -from pyramid.compat import md5 -from pyramid.events import ApplicationCreated -from pyramid.exceptions import ConfigurationError -from pyramid.exceptions import Forbidden -from pyramid.exceptions import NotFound -from pyramid.exceptions import PredicateMismatch -from pyramid.i18n import get_localizer -from pyramid.log import make_stream_logger -from pyramid.mako_templating import renderer_factory as mako_renderer_factory +from pyramid.config import Configurator as BaseConfigurator +from pyramid.config import ConfigurationError # API +from pyramid.config import DEFAULT_RENDERERS from pyramid.path import caller_package -from pyramid.path import package_path -from pyramid.registry import Registry -from pyramid.renderers import RendererHelper -from pyramid.request import route_request_iface -from pyramid.resource import PackageOverrides -from pyramid.resource import resolve_resource_spec -from pyramid.settings import Settings -from pyramid.static import StaticURLInfo -from pyramid.threadlocal import get_current_registry -from pyramid.threadlocal import get_current_request -from pyramid.threadlocal import manager -from pyramid.traversal import DefaultRootFactory -from pyramid.traversal import find_interface -from pyramid.traversal import traversal_path -from pyramid.urldispatch import RoutesMapper -from pyramid.util import DottedNameResolver -from pyramid.view import default_exceptionresponse_view -from pyramid.view import render_view_to_response - -MAX_ORDER = 1 << 30 -DEFAULT_PHASH = md5().hexdigest() - -DEFAULT_RENDERERS = ( - ('.mak', mako_renderer_factory), - ('.mako', mako_renderer_factory), - ('json', renderers.json_renderer_factory), - ('string', renderers.string_renderer_factory), - ) - -if chameleon_text: - DEFAULT_RENDERERS += (('.pt', chameleon_zpt.renderer_factory),) -if chameleon_zpt: - DEFAULT_RENDERERS += (('.txt', chameleon_text.renderer_factory),) - - -class Configurator(object): - """ - A Configurator is used to configure a :app:`Pyramid` - :term:`application registry`. - - The Configurator accepts a number of arguments: ``registry``, - ``package``, ``settings``, ``root_factory``, - ``authentication_policy``, ``authorization_policy``, ``renderers`` - ``debug_logger``, ``locale_negotiator``, ``request_factory``, and - ``renderer_globals_factory``. - - If the ``registry`` argument is passed as a non-``None`` value, it - must be an instance of the :class:`pyramid.registry.Registry` - class representing the registry to configure. If ``registry`` is - ``None``, the configurator will create a - :class:`pyramid.registry.Registry` instance itself; it will - also perform some default configuration that would not otherwise - be done. After construction, the configurator may be used to add - configuration to the registry. The overall state of a registry is - called the 'configuration state'. - - .. warning:: If a ``registry`` is passed to the Configurator - constructor, all other constructor arguments except ``package`` - are ignored. - - If the ``package`` argument is passed, it must be a reference to a - Python :term:`package` (e.g. ``sys.modules['thepackage']``) or a - :term:`dotted Python name` to same. This value is used as a basis - to convert relative paths passed to various configuration methods, - such as methods which accept a ``renderer`` argument, into - absolute paths. If ``None`` is passed (the default), the package - is assumed to be the Python package in which the *caller* of the - ``Configurator`` constructor lives. - - If the ``settings`` argument is passed, it should be a Python dictionary - representing the deployment settings for this application. These are - later retrievable using the :attr:`pyramid.registry.Registry.settings` - attribute (aka ``request.registry.settings``). - - If the ``root_factory`` argument is passed, it should be an object - representing the default :term:`root factory` for your application - or a :term:`dotted Python name` to same. If it is ``None``, a - default root factory will be used. - - If ``authentication_policy`` is passed, it should be an instance - of an :term:`authentication policy` or a :term:`dotted Python - name` to same. - - If ``authorization_policy`` is passed, it should be an instance of - an :term:`authorization policy` or a :term:`dotted Python name` to - same. - - .. note:: A ``ConfigurationError`` will be raised when an - authorization policy is supplied without also supplying an - authentication policy (authorization requires authentication). - - If ``renderers`` is passed, it should be a list of tuples - representing a set of :term:`renderer` factories which should be - configured into this application (each tuple representing a set of - positional values that should be passed to - :meth:`pyramid.configuration.Configurator.add_renderer`). If - it is not passed, a default set of renderer factories is used. - - If ``debug_logger`` is not passed, a default debug logger that - logs to stderr will be used. If it is passed, it should be an - instance of the :class:`logging.Logger` (PEP 282) standard library - class or a :term:`dotted Python name` to same. The debug logger - is used by :app:`Pyramid` itself to log warnings and - authorization debugging information. - - If ``locale_negotiator`` is passed, it should be a :term:`locale - negotiator` implementation or a :term:`dotted Python name` to - same. See :ref:`custom_locale_negotiator`. - If ``request_factory`` is passed, it should be a :term:`request - factory` implementation or a :term:`dotted Python name` to same. - See :ref:`custom_request_factory`. By default it is ``None``, - which means use the default request factory. +from zope.deprecation import deprecated - If ``renderer_globals_factory`` is passed, it should be a - :term:`renderer globals` factory implementation or a :term:`dotted - Python name` to same. See :ref:`custom_renderer_globals_factory`. - By default, it is ``None``, which means use no renderer globals - factory. +ConfigurationError = ConfigurationError # pyflakes - If ``default_permission`` is passed, it should be a - :term:`permission` string to be used as the default permission for - all view configuration registrations performed against this - Configurator. An example of a permission string:``'view'``. - Adding a default permission makes it unnecessary to protect each - view configuration with an explicit permission, unless your - application policy requires some exception for a particular view. - By default, ``default_permission`` is ``None``, meaning that view - configurations which do not explicitly declare a permission will - always be executable by entirely anonymous users (any - authorization policy in effect is ignored). See also - :ref:`setting_a_default_permission`. - - If ``session_factory`` is passed, it should be an object which - implements the :term:`session factory` interface. If a nondefault - value is passed, the ``session_factory`` will be used to create a - session object when ``request.session`` is accessed. Note that - the same outcome can be achieved by calling - :ref:`pyramid.configration.Configurator.set_session_factory`. By - default, this argument is ``None``, indicating that no session - factory will be configured (and thus accessing ``request.session`` - will throw an error) unless ``set_session_factory`` is called later - during configuration. """ - - manager = manager # for testing injection - venusian = venusian # for testing injection +deprecated( + 'ConfigurationError', + 'pyramid.configuration.ConfigurationError is deprecated as of ' + 'Pyramid 1.0. Use ``pyramid.config.ConfigurationError`` instead.') +class Configurator(BaseConfigurator): def __init__(self, registry=None, package=None, @@ -227,2525 +27,30 @@ class Configurator(object): renderer_globals_factory=None, default_permission=None, session_factory=None, + autocommit=True, ): if package is None: package = caller_package() - name_resolver = DottedNameResolver(package) - self.name_resolver = name_resolver - self.package_name = name_resolver.package_name - self.package = name_resolver.package - self.registry = registry - if registry is None: - registry = Registry(self.package_name) - self.registry = registry - self.setup_registry( - settings=settings, - root_factory=root_factory, - authentication_policy=authentication_policy, - authorization_policy=authorization_policy, - renderers=renderers, - debug_logger=debug_logger, - locale_negotiator=locale_negotiator, - request_factory=request_factory, - renderer_globals_factory=renderer_globals_factory, - default_permission=default_permission, - session_factory=session_factory, - ) - - def _set_settings(self, mapping): - settings = Settings(mapping or {}) - self.registry.settings = settings - return settings - - def _set_root_factory(self, factory): - """ Add a :term:`root factory` to the current configuration - state. If the ``factory`` argument is ``None`` a default root - factory will be registered.""" - factory = self.maybe_dotted(factory) - if factory is None: - factory = DefaultRootFactory - self.registry.registerUtility(factory, IRootFactory) - self.registry.registerUtility(factory, IDefaultRootFactory) # b/c - - def _set_authentication_policy(self, policy, _info=u''): - """ Add a :app:`Pyramid` :term:`authentication policy` to - the current configuration.""" - policy = self.maybe_dotted(policy) - self.registry.registerUtility(policy, IAuthenticationPolicy, - info=_info) - - def _set_authorization_policy(self, policy, _info=u''): - """ Add a :app:`Pyramid` :term:`authorization policy` to - the current configuration state (also accepts a :term:`dotted - Python name`.""" - policy = self.maybe_dotted(policy) - self.registry.registerUtility(policy, IAuthorizationPolicy, info=_info) - - def _make_spec(self, path_or_spec): - package, filename = resolve_resource_spec(path_or_spec, - self.package_name) - if package is None: - return filename # absolute filename - return '%s:%s' % (package, filename) - - def _split_spec(self, path_or_spec): - return resolve_resource_spec(path_or_spec, self.package_name) - - def _derive_view(self, view, permission=None, predicates=(), - attr=None, renderer=None, wrapper_viewname=None, - viewname=None, accept=None, order=MAX_ORDER, - phash=DEFAULT_PHASH): - if renderer is None: # use default renderer if one exists - default_renderer_factory = self.registry.queryUtility( - IRendererFactory) - if default_renderer_factory is not None: - renderer = {'name':None, 'package':self.package} - view = self.maybe_dotted(view) - authn_policy = self.registry.queryUtility(IAuthenticationPolicy) - authz_policy = self.registry.queryUtility(IAuthorizationPolicy) - settings = self.registry.settings - logger = self.registry.queryUtility(IDebugLogger) - mapped_view = _map_view(view, self.registry, attr, renderer) - owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname) - secured_view = _secure_view(owrapped_view, permission, - authn_policy, authz_policy) - debug_view = _authdebug_view(secured_view, permission, - authn_policy, authz_policy, settings, - logger) - predicated_view = _predicate_wrap(debug_view, predicates) - derived_view = _attr_wrap(predicated_view, accept, order, phash) - return derived_view - - def _override(self, package, path, override_package, override_prefix, - _info=u'', 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, info=_info) - override.insert(path, override_pkg_name, override_prefix) - - def _set_security_policies(self, authentication, authorization=None): - if authorization is None: - authorization = ACLAuthorizationPolicy() # default - if authorization and not authentication: - raise ConfigurationError( - 'If the "authorization" is passed a value, ' - 'the "authentication" argument must also be ' - 'passed a value; authorization requires authentication.') - self._set_authentication_policy(authentication) - self._set_authorization_policy(authorization) - - def _fix_registry(self): - """ Fix up a ZCA component registry that is not a - pyramid.registry.Registry by adding analogues of ``has_listeners``, - and ``notify`` through monkey-patching.""" - - _registry = self.registry - - if not hasattr(_registry, 'notify'): - def notify(*events): - [ _ for _ in _registry.subscribers(events, None) ] - _registry.notify = notify - - if not hasattr(_registry, 'has_listeners'): - _registry.has_listeners = True - - # API - - def with_package(self, package): - """ Return a new Configurator instance with the same registry - as this configurator using the package supplied as the - ``package`` argument to the new configurator. ``package`` may - be an actual Python package object or a Python dotted name - representing a package.""" - return self.__class__(registry=self.registry, package=package) - - def maybe_dotted(self, dotted): - """ Resolve the :term:`dotted Python name` ``dotted`` to a - global Python object. If ``dotted`` is not a string, return - it without attempting to do any name resolution. If - ``dotted`` is a relative dotted name (e.g. ``.foo.bar``, - consider it relative to the ``package`` argument supplied to - this Configurator's constructor.""" - return self.name_resolver.maybe_resolve(dotted) - - def absolute_resource_spec(self, relative_spec): - """ Resolve the potentially relative :term:`resource - specification` string passed as ``relative_spec`` into an - absolute resource specification string and return the string. - Use the ``package`` of this configurator as the package to - which the resource specification will be considered relative - when generating an absolute resource specification. If the - provided ``relative_spec`` argument is already absolute, or if - the ``relative_spec`` is not a string, it is simply returned.""" - if not isinstance(relative_spec, basestring): - return relative_spec - return self._make_spec(relative_spec) - - def setup_registry(self, settings=None, root_factory=None, - authentication_policy=None, authorization_policy=None, - renderers=DEFAULT_RENDERERS, debug_logger=None, - locale_negotiator=None, request_factory=None, - renderer_globals_factory=None, - default_permission=None, - session_factory=None): - """ When you pass a non-``None`` ``registry`` argument to the - :term:`Configurator` constructor, no initial 'setup' is - performed against the registry. This is because the registry - you pass in may have already been initialized for use under - :app:`Pyramid` via a different configurator. However, in - some circumstances, such as when you want to use the Zope - 'global` registry instead of a registry created as a result of - the Configurator constructor, or when you want to reset the - initial setup of a registry, you *do* want to explicitly - initialize the registry associated with a Configurator for use - under :app:`Pyramid`. Use ``setup_registry`` to do this - initialization. - - ``setup_registry`` configures settings, a root factory, - security policies, renderers, a debug logger, a locale - negotiator, and various other settings using the - configurator's current registry, as per the descriptions in - the Configurator constructor.""" - self._fix_registry() - self._set_settings(settings) - self._set_root_factory(root_factory) - debug_logger = self.maybe_dotted(debug_logger) - if debug_logger is None: - debug_logger = make_stream_logger('pyramid.debug', sys.stderr) - registry = self.registry - registry.registerUtility(debug_logger, IDebugLogger) - if authentication_policy or authorization_policy: - self._set_security_policies(authentication_policy, - authorization_policy) - for name, renderer in renderers: - self.add_renderer(name, renderer) - self.add_view(default_exceptionresponse_view, - context=IExceptionResponse) - if locale_negotiator: - locale_negotiator = self.maybe_dotted(locale_negotiator) - registry.registerUtility(locale_negotiator, ILocaleNegotiator) - if request_factory: - request_factory = self.maybe_dotted(request_factory) - self.set_request_factory(request_factory) - if renderer_globals_factory: - renderer_globals_factory = self.maybe_dotted( - renderer_globals_factory) - self.set_renderer_globals_factory(renderer_globals_factory) - if default_permission: - self.set_default_permission(default_permission) - if session_factory is not None: - self.set_session_factory(session_factory) - - # getSiteManager is a unit testing dep injection - def hook_zca(self, getSiteManager=None): - """ Call :func:`zope.component.getSiteManager.sethook` with - the argument - :data:`pyramid.threadlocal.get_current_registry`, causing - the :term:`Zope Component Architecture` 'global' APIs such as - :func:`zope.component.getSiteManager`, - :func:`zope.component.getAdapter` and others to use the - :app:`Pyramid` :term:`application registry` rather than the - Zope 'global' registry. If :mod:`zope.component` cannot be - imported, this method will raise an :exc:`ImportError`.""" - if getSiteManager is None: - from zope.component import getSiteManager - getSiteManager.sethook(get_current_registry) - - # getSiteManager is a unit testing dep injection - def unhook_zca(self, getSiteManager=None): - """ Call :func:`zope.component.getSiteManager.reset` to undo - the action of - :meth:`pyramid.configuration.Configurator.hook_zca`. If - :mod:`zope.component` cannot be imported, this method will - raise an :exc:`ImportError`.""" - if getSiteManager is None: # pragma: no cover - from zope.component import getSiteManager - getSiteManager.reset() - - def begin(self, request=None): - """ Indicate that application or test configuration has begun. - This pushes a dictionary containing the :term:`application - registry` implied by ``registry`` attribute of this - configurator and the :term:`request` implied by the - ``request`` argument on to the :term:`thread local` stack - consulted by various :mod:`pyramid.threadlocal` API - functions.""" - self.manager.push({'registry':self.registry, 'request':request}) - - def end(self): - """ Indicate that application or test configuration has ended. - This pops the last value pushed on to the :term:`thread local` - stack (usually by the ``begin`` method) and returns that - value. - """ - return self.manager.pop() - - def derive_view(self, view, attr=None, renderer=None): - """ - - Create a :term:`view callable` using the function, instance, - or class (or :term:`dotted Python name` referring to the same) - provided as ``view`` object. - - This is API is useful to framework extenders who create - pluggable systems which need to register 'proxy' view - callables for functions, instances, or classes which meet the - requirements of being a :app:`Pyramid` view callable. For - example, a ``some_other_framework`` function in another - framework may want to allow a user to supply a view callable, - but he may want to wrap the view callable in his own before - registering the wrapper as a :app:`Pyramid` view callable. - Because a :app:`Pyramid` view callable can be any of a - number of valid objects, the framework extender will not know - how to call the user-supplied object. Running it through - ``derive_view`` normalizes it to a callable which accepts two - arguments: ``context`` and ``request``. - - For example: - - .. code-block:: python - - def some_other_framework(user_supplied_view): - config = Configurator(reg) - proxy_view = config.derive_view(user_supplied_view) - def my_wrapper(context, request): - do_something_that_mutates(request) - return proxy_view(context, request) - config.add_view(my_wrapper) - - The ``view`` object provided should be one of the following: - - - A function or another non-class callable object that accepts - a :term:`request` as a single positional argument and which - returns a :term:`response` object. - - - A function or other non-class callable object that accepts - two positional arguments, ``context, request`` and which - returns a :term:`response` object. - - - A class which accepts a single positional argument in its - constructor named ``request``, and which has a ``__call__`` - method that accepts no arguments that returns a - :term:`response` object. - - - A class which accepts two positional arguments named - ``context, request``, and which has a ``__call__`` method - that accepts no arguments that returns a :term:`response` - object. - - - A :term:`dotted Python name` which refers to any of the - kinds of objects above. - - This API returns a callable which accepts the arguments - ``context, request`` and which returns the result of calling - the provided ``view`` object. - - The ``attr`` keyword argument is most useful when the view - object is a class. It names the method that should be used as - the callable. If ``attr`` is not provided, the attribute - effectively defaults to ``__call__``. See - :ref:`class_as_view` for more information. - - The ``renderer`` keyword argument should be a renderer - name. If supplied, it will cause the returned callable to use - a :term:`renderer` to convert the user-supplied view result to - a :term:`response` object. If a ``renderer`` argument is not - supplied, the user-supplied view must itself return a - :term:`response` object. """ - - if renderer is not None and not isinstance(renderer, dict): - renderer = {'name':renderer, 'package':self.package} - return self._derive_view(view, attr=attr, renderer=renderer) - - def add_subscriber(self, subscriber, iface=None, info=u''): - """Add an event :term:`subscriber` for the event stream - implied by the supplied ``iface`` interface. The - ``subscriber`` argument represents a callable object (or a - :term:`dotted Python name` which identifies a callable); it - will be called with a single object ``event`` whenever - :app:`Pyramid` emits an :term:`event` associated with the - ``iface``, which may be an :term:`interface` or a class or a - :term:`dotted Python name` to a global object representing an - interface or a class. Using the default ``iface`` value, - ``None`` will cause the subscriber to be registered for all - event types. See :ref:`events_chapter` for more information - about events and subscribers.""" - dotted = self.maybe_dotted - subscriber, iface = dotted(subscriber), dotted(iface) - if iface is None: - iface = (Interface,) - if not isinstance(iface, (tuple, list)): - iface = (iface,) - self.registry.registerHandler(subscriber, iface, info=info) - return subscriber - - def add_settings(self, settings=None, **kw): - """Augment the ``settings`` argument passed in to the Configurator - constructor with one or more 'setting' key/value pairs. A setting is - a single key/value pair in the dictionary-ish object returned from - the API :attr:`pyramid.registry.Registry.settings` and - :meth:`pyramid.configuration.Configurator.get_settings`. - - You may pass a dictionary:: - - config.add_settings({'external_uri':'http://example.com'}) - - Or a set of key/value pairs:: - - config.add_settings(external_uri='http://example.com') - - This function is useful when you need to test code that accesses the - :attr:`pyramid.registry.Registry.settings` API (or the - :meth:`pyramid.configuration.Configurator.get_settings` API) and - which uses values from that API. - """ - if settings is None: - settings = {} - utility = self.registry.settings - if utility is None: - utility = self._set_settings(settings) - utility.update(settings) - utility.update(kw) - - def get_settings(self): - """ - Return a 'settings' object for the current application. A - 'settings' object is a dictionary-like object that contains - key/value pairs based on the dictionary passed as the ``settings`` - argument to the :class:`pyramid.configuration.Configurator` - constructor or the :func:`pyramid.router.make_app` API. - - .. note:: For backwards compatibility, dictionary keys can also be - looked up as attributes of the settings object. - - .. note:: the :attr:`pyramid.registry.Registry.settings` API - performs the same duty. - """ - return self.registry.settings - - def make_wsgi_app(self): - """ Returns a :app:`Pyramid` WSGI application representing - the current configuration state and sends a - :class:`pyramid.events.ApplicationCreated` - event to all listeners.""" - from pyramid.router import Router # avoid circdep - app = Router(self.registry) - # We push the registry on to the stack here in case any code - # that depends on the registry threadlocal APIs used in - # listeners subscribed to the IApplicationCreated event. - self.manager.push({'registry':self.registry, 'request':None}) - try: - self.registry.notify(ApplicationCreated(app)) - finally: - self.manager.pop() - return app - - def load_zcml(self, spec='configure.zcml', lock=threading.Lock()): - """ Load configuration from a :term:`ZCML` file into the - current configuration state. The ``spec`` argument is an - absolute filename, a relative filename, or a :term:`resource - specification`, defaulting to ``configure.zcml`` (relative to - the package of the configurator's caller).""" - package_name, filename = self._split_spec(spec) - if package_name is None: # absolute filename - package = self.package - else: - __import__(package_name) - package = sys.modules[package_name] - - lock.acquire() - self.manager.push({'registry':self.registry, 'request':None}) - try: - context = ConfigurationMachine() - registerCommonDirectives(context) - context.package = package - context.registry = self.registry - xmlconfig.file(filename, package, context=context, execute=True) - finally: - lock.release() - self.manager.pop() - return self.registry - - def add_handler(self, route_name, pattern, handler, action=None, **kw): - - """ Add a Pylons-style view handler. This function adds a - route and some number of views based on a handler object - (usually a class). - - ``route_name`` is the name of the route (to be used later in - URL generation). - - ``pattern`` is the matching pattern, - e.g. ``'/blog/{action}'``. ``pattern`` may be ``None``, in - which case the pattern of an existing route named the same as - ``route_name`` is used. If ``pattern`` is ``None`` and no - route named ``route_name`` exists, a ``ConfigurationError`` is - raised. - - ``handler`` is a dotted name of (or direct reference to) a - Python handler class, - e.g. ``'my.package.handlers.MyHandler'``. - - If ``{action}`` or ``:action`` is in - the pattern, the exposed methods of the handler will be used - as views. - - If ``action`` is passed, it will be considered the method name - of the handler to use as a view. - - Passing both ``action`` and having an ``{action}`` in the - route pattern is disallowed. - - Any extra keyword arguments are passed along to ``add_route``. - - See :ref:`handlers_chapter` for more explanatory documentation. - - This method returns the result of add_route.""" - handler = self.maybe_dotted(handler) - - if pattern is not None: - route = self.add_route(route_name, pattern, **kw) - else: - mapper = self.get_routes_mapper() - route = mapper.get_route(route_name) - if route is None: - raise ConfigurationError( - 'The "pattern" parameter may only be "None" when a route ' - 'with the route_name argument was previously registered. ' - 'No such route named %r exists' % route_name) - - pattern = route.pattern - - path_has_action = ':action' in pattern or '{action}' in pattern - - if action and path_has_action: - raise ConfigurationError( - 'action= (%r) disallowed when an action is in the route ' - 'path %r' % (action, pattern)) - - if path_has_action: - autoexpose = getattr(handler, '__autoexpose__', r'[A-Za-z]+') - if autoexpose: - try: - autoexpose = re.compile(autoexpose).match - except (re.error, TypeError), why: - raise ConfigurationError(why[0]) - for method_name, method in inspect.getmembers( - handler, inspect.ismethod): - configs = getattr(method, '__exposed__', []) - if autoexpose and not configs: - if autoexpose(method_name): - configs = [{}] - for expose_config in configs: - # we don't want to mutate any dict in __exposed__, - # so we copy each - view_args = expose_config.copy() - action = view_args.pop('name', method_name) - preds = list(view_args.pop('custom_predicates', [])) - preds.append(ActionPredicate(action)) - view_args['custom_predicates'] = preds - self.add_view(view=handler, attr=method_name, - route_name=route_name, **view_args) - else: - method_name = action - if method_name is None: - method_name = '__call__' - - # Scan the controller for any other methods with this action name - for meth_name, method in inspect.getmembers( - handler, inspect.ismethod): - configs = getattr(method, '__exposed__', [{}]) - for expose_config in configs: - # Don't re-register the same view if this method name is - # the action name - if meth_name == action: - continue - # We only reg a view if the name matches the action - if expose_config.get('name') != method_name: - continue - # we don't want to mutate any dict in __exposed__, - # so we copy each - view_args = expose_config.copy() - del view_args['name'] - self.add_view(view=handler, attr=meth_name, - route_name=route_name, **view_args) - - # Now register the method itself - method = getattr(handler, method_name, None) - configs = getattr(method, '__exposed__', [{}]) - for expose_config in configs: - self.add_view(view=handler, attr=action, route_name=route_name, - **expose_config) - - return route - - def add_view(self, view=None, name="", for_=None, permission=None, - request_type=None, route_name=None, request_method=None, - request_param=None, containment=None, attr=None, - renderer=None, wrapper=None, xhr=False, accept=None, - header=None, path_info=None, custom_predicates=(), - context=None, _info=u''): - """ Add a :term:`view configuration` to the current - configuration state. Arguments to ``add_view`` are broken - down below into *predicate* arguments and *non-predicate* - arguments. Predicate arguments narrow the circumstances in - which the view callable will be invoked when a request is - presented to :app:`Pyramid`; non-predicate arguments are - informational. - - Non-Predicate Arguments - - view - - A :term:`view callable` or a :term:`dotted Python name` - which refers to a view callable. This argument is required - unless a ``renderer`` argument also exists. If a - ``renderer`` argument is passed, and a ``view`` argument is - not provided, the view callable defaults to a callable that - returns an empty dictionary (see - :ref:`views_which_use_a_renderer`). - - permission - - The name of a :term:`permission` that the user must possess - in order to invoke the :term:`view callable`. See - :ref:`view_security_section` for more information about view - security and permissions. If ``permission`` is omitted, a - *default* permission may be used for this view registration - if one was named as the - :class:`pyramid.configuration.Configurator` constructor's - ``default_permission`` argument, or if - :meth:`pyramid.configuration.Configurator.set_default_permission` - was used prior to this view registration. Pass the string - ``__no_permission_required__`` as the permission argument to - explicitly indicate that the view should always be - executable by entirely anonymous users, regardless of the - default permission, bypassing any :term:`authorization - policy` that may be in effect. - - attr - - The view machinery defaults to using the ``__call__`` method - of the :term:`view callable` (or the function itself, if the - view callable is a function) to obtain a response. The - ``attr`` value allows you to vary the method attribute used - to obtain the response. For example, if your view was a - class, and the class has a method named ``index`` and you - wanted to use this method instead of the class' ``__call__`` - method to return the response, you'd say ``attr="index"`` in the - view configuration for the view. This is - most useful when the view definition is a class. - - renderer - - This is either a single string term (e.g. ``json``) or a - string implying a path or :term:`resource specification` - (e.g. ``templates/views.pt``) naming a :term:`renderer` - implementation. If the ``renderer`` value does not contain - a dot ``.``, the specified string will be used to look up a - renderer implementation, and that renderer implementation - will be used to construct a response from the view return - value. If the ``renderer`` value contains a dot (``.``), - the specified term will be treated as a path, and the - filename extension of the last element in the path will be - used to look up the renderer implementation, which will be - passed the full path. The renderer implementation will be - used to construct a :term:`response` from the view return - value. - - Note that if the view itself returns a :term:`response` (see - :ref:`the_response`), the specified renderer implementation - is never called. - - When the renderer is a path, although a path is usually just - a simple relative pathname (e.g. ``templates/foo.pt``, - implying that a template named "foo.pt" is in the - "templates" directory relative to the directory of the - current :term:`package` of the Configurator), a path can be - absolute, starting with a slash on UNIX or a drive letter - prefix on Windows. The path can alternately be a - :term:`resource specification` in the form - ``some.dotted.package_name:relative/path``, making it - possible to address template resources which live in a - separate package. - - The ``renderer`` attribute is optional. If it is not - defined, the "null" renderer is assumed (no rendering is - performed and the value is passed back to the upstream - :app:`Pyramid` machinery unmolested). - - wrapper - - The :term:`view name` of a different :term:`view - configuration` which will receive the response body of this - view as the ``request.wrapped_body`` attribute of its own - :term:`request`, and the :term:`response` returned by this - view as the ``request.wrapped_response`` attribute of its - own request. Using a wrapper makes it possible to "chain" - views together to form a composite response. The response - of the outermost wrapper view will be returned to the user. - The wrapper view will be found as any view is found: see - :ref:`view_lookup`. The "best" wrapper view will be found - based on the lookup ordering: "under the hood" this wrapper - view is looked up via - ``pyramid.view.render_view_to_response(context, request, - 'wrapper_viewname')``. The context and request of a wrapper - view is the same context and request of the inner view. If - this attribute is unspecified, no view wrapping is done. - - Predicate Arguments - - name - - The :term:`view name`. Read :ref:`traversal_chapter` to - understand the concept of a view name. - - context - - An object or a :term:`dotted Python name` referring to an - interface or class object that the :term:`context` must be - an instance of, *or* the :term:`interface` that the - :term:`context` must provide in order for this view to be - found and called. This predicate is true when the - :term:`context` is an instance of the represented class or - if the :term:`context` provides the represented interface; - it is otherwise false. This argument may also be provided - to ``add_view`` as ``for_`` (an older, still-supported - spelling). - - route_name - - This value must match the ``name`` of a :term:`route - configuration` declaration (see :ref:`urldispatch_chapter`) - that must match before this view will be called. Note that - the ``route`` configuration referred to by ``route_name`` - usually has a ``*traverse`` token in the value of its - ``path``, representing a part of the path that will be used - by :term:`traversal` against the result of the route's - :term:`root factory`. - - .. warning:: Using this argument services an advanced - feature that isn't often used unless you want to perform - traversal *after* a route has matched. See - :ref:`hybrid_chapter` for more information on using this - advanced feature. - - request_type - - This value should be an :term:`interface` that the - :term:`request` must provide in order for this view to be - found and called. This value exists only for backwards - compatibility purposes. - - request_method - - This value can either be one of the strings ``GET``, - ``POST``, ``PUT``, ``DELETE``, or ``HEAD`` representing an - HTTP ``REQUEST_METHOD``. A view declaration with this - argument ensures that the view will only be called when the - request's ``method`` attribute (aka the ``REQUEST_METHOD`` of - the WSGI environment) string matches the supplied value. - - request_param - - This value can be any string. A view declaration with this - argument ensures that the view will only be called when the - :term:`request` has a key in the ``request.params`` - dictionary (an HTTP ``GET`` or ``POST`` variable) that has a - name which matches the supplied value. If the value - supplied has a ``=`` sign in it, - e.g. ``request_params="foo=123"``, then the key (``foo``) - must both exist in the ``request.params`` dictionary, *and* - the value must match the right hand side of the expression - (``123``) for the view to "match" the current request. - - containment - - This value should be a Python class or :term:`interface` or - a :term:`dotted Python name` to such an object that a parent - object in the :term:`lineage` must provide in order for this - view to be found and called. The nodes in your object graph - must be "location-aware" to use this feature. See - :ref:`location_aware` for more information about - location-awareness. - - xhr - - This value should be either ``True`` or ``False``. If this - value is specified and is ``True``, the :term:`request` - must possess an ``HTTP_X_REQUESTED_WITH`` (aka - ``X-Requested-With``) header that has the value - ``XMLHttpRequest`` for this view to be found and called. - This is useful for detecting AJAX requests issued from - jQuery, Prototype and other Javascript libraries. - - accept - - The value of this argument represents a match query for one - or more mimetypes in the ``Accept`` HTTP request header. If - this value is specified, it must be in one of the following - forms: a mimetype match token in the form ``text/plain``, a - wildcard mimetype match token in the form ``text/*`` or a - match-all wildcard mimetype match token in the form ``*/*``. - If any of the forms matches the ``Accept`` header of the - request, this predicate will be true. - - header - - This value represents an HTTP header name or a header - name/value pair. If the value contains a ``:`` (colon), it - will be considered a name/value pair - (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). The - value portion should be a regular expression. If the value - does not contain a colon, the entire value will be - considered to be the header name - (e.g. ``If-Modified-Since``). If the value evaluates to a - header name only without a value, the header specified by - the name must be present in the request for this predicate - to be true. If the value evaluates to a header name/value - pair, the header specified by the name must be present in - the request *and* the regular expression specified as the - value must match the header value. Whether or not the value - represents a header name or a header name/value pair, the - case of the header name is not significant. - - path_info - - This value represents a regular expression pattern that will - be tested against the ``PATH_INFO`` WSGI environment - variable. If the regex matches, this predicate will be - ``True``. - - - custom_predicates - - This value should be a sequence of references to custom - predicate callables. Use custom predicates when no set of - predefined predicates do what you need. Custom predicates - can be combined with predefined predicates as necessary. - Each custom predicate callable should accept two arguments: - ``context`` and ``request`` and should return either - ``True`` or ``False`` after doing arbitrary evaluation of - the context and/or the request. If all callables return - ``True``, the associated view callable will be considered - viable for a given request. - - """ - view = self.maybe_dotted(view) - context = self.maybe_dotted(context) - for_ = self.maybe_dotted(for_) - containment = self.maybe_dotted(containment) - - if not view: - if renderer: - def view(context, request): - return {} - else: - raise ConfigurationError('"view" was not specified and ' - 'no "renderer" specified') - - if request_type is not None: - request_type = self.maybe_dotted(request_type) - if not IInterface.providedBy(request_type): - raise ConfigurationError( - 'request_type must be an interface, not %s' % request_type) - - request_iface = IRequest - - if route_name is not None: - request_iface = self.registry.queryUtility(IRouteRequest, - name=route_name) - if request_iface is None: - deferred_views = getattr(self.registry, - 'deferred_route_views', None) - if deferred_views is None: - deferred_views = self.registry.deferred_route_views = {} - info = dict( - view=view, name=name, for_=for_, permission=permission, - request_type=request_type, route_name=route_name, - request_method=request_method, request_param=request_param, - containment=containment, attr=attr, - renderer=renderer, wrapper=wrapper, xhr=xhr, accept=accept, - header=header, path_info=path_info, - custom_predicates=custom_predicates, context=context, - _info=u'' - ) - view_info = deferred_views.setdefault(route_name, []) - view_info.append(info) - return - - order, predicates, phash = _make_predicates(xhr=xhr, - request_method=request_method, path_info=path_info, - request_param=request_param, header=header, accept=accept, - containment=containment, request_type=request_type, - custom=custom_predicates) - - if permission is None: - # intent: will be None if no default permission is registered - permission = self.registry.queryUtility(IDefaultPermission) - - if renderer is not None and not isinstance(renderer, dict): - renderer = {'name':renderer, 'package':self.package} - - # NO_PERMISSION_REQUIRED handled by _secure_view - derived_view = self._derive_view(view, permission, predicates, attr, - renderer, wrapper, name, accept, - order, phash) - - if context is None: - context = for_ - - r_context = context - if r_context is None: - r_context = Interface - if not IInterface.providedBy(r_context): - r_context = implementedBy(r_context) - - registered = self.registry.adapters.registered - - # A multiviews is a set of views which are registered for - # exactly the same context type/request type/name triad. Each - # consituent view in a multiview differs only by the - # predicates which it possesses. - - # To find a previously registered view for a context - # type/request type/name triad, we need to use the - # ``registered`` method of the adapter registry rather than - # ``lookup``. ``registered`` ignores interface inheritance - # for the required and provided arguments, returning only a - # view registered previously with the *exact* triad we pass - # in. - - # We need to do this three times, because we use three - # different interfaces as the ``provided`` interface while - # doing registrations, and ``registered`` performs exact - # matches on all the arguments it receives. - - old_view = None - - for view_type in (IView, ISecuredView, IMultiView): - old_view = registered((IViewClassifier, request_iface, r_context), - view_type, name) - if old_view is not None: - break - - isexc = isexception(context) - - def regclosure(): - if hasattr(derived_view, '__call_permissive__'): - view_iface = ISecuredView - else: - view_iface = IView - self.registry.registerAdapter( - derived_view, - (IViewClassifier, request_iface, context), - view_iface, name, info=_info) - if isexc: - self.registry.registerAdapter( - derived_view, - (IExceptionViewClassifier, request_iface, context), - view_iface, name, info=_info) - - is_multiview = IMultiView.providedBy(old_view) - old_phash = getattr(old_view, '__phash__', DEFAULT_PHASH) - - if old_view is None: - # - No component was yet registered for any of our I*View - # interfaces exactly; this is the first view for this - # triad. - regclosure() - - elif (not is_multiview) and (old_phash == phash): - # - A single view component was previously registered with - # the same predicate hash as this view; this registration - # is therefore an override. - regclosure() - - else: - # - A view or multiview was already registered for this - # triad, and the new view is not an override. - - # XXX we could try to be more efficient here and register - # a non-secured view for a multiview if none of the - # multiview's consituent views have a permission - # associated with them, but this code is getting pretty - # rough already - if is_multiview: - multiview = old_view - else: - multiview = MultiView(name) - old_accept = getattr(old_view, '__accept__', None) - old_order = getattr(old_view, '__order__', MAX_ORDER) - multiview.add(old_view, old_order, old_accept, old_phash) - multiview.add(derived_view, order, accept, phash) - for view_type in (IView, ISecuredView): - # unregister any existing views - self.registry.adapters.unregister( - (IViewClassifier, request_iface, r_context), - view_type, name=name) - if isexc: - self.registry.adapters.unregister( - (IExceptionViewClassifier, request_iface, r_context), - view_type, name=name) - self.registry.registerAdapter( - multiview, - (IViewClassifier, request_iface, context), - IMultiView, name=name, info=_info) - if isexc: - self.registry.registerAdapter( - multiview, - (IExceptionViewClassifier, request_iface, context), - IMultiView, name=name, info=_info) - - def add_route(self, - name, - pattern=None, - view=None, - view_for=None, - permission=None, - factory=None, - for_=None, - header=None, - xhr=False, - accept=None, - path_info=None, - request_method=None, - request_param=None, - traverse=None, - custom_predicates=(), - view_permission=None, - renderer=None, - view_renderer=None, - view_context=None, - view_attr=None, - use_global_views=False, - path=None, - pregenerator=None, - _info=u''): - """ Add a :term:`route configuration` to the current - configuration state, as well as possibly a :term:`view - configuration` to be used to specify a :term:`view callable` - that will be invoked when this route matches. The arguments - to this method are divided into *predicate*, *non-predicate*, - and *view-related* types. :term:`Route predicate` arguments - narrow the circumstances in which a route will be match a - request; non-predicate arguments are informational. - - Non-Predicate Arguments - - name - - The name of the route, e.g. ``myroute``. This attribute is - required. It must be unique among all defined routes in a given - application. - - factory - - A Python object (often a function or a class) or a - :term:`dotted Python name` which refers to the same object - that will generate a :app:`Pyramid` :term:`context` - object when this route matches. For example, - ``mypackage.models.MyFactoryClass``. If this argument is - not specified, a default root factory will be used. - - traverse - - If you would like to cause the :term:`context` to be - something other than the :term:`root` object when this route - matches, you can spell a traversal pattern as the - ``traverse`` argument. This traversal pattern will be used - as the traversal path: traversal will begin at the root - object implied by this route (either the global root, or the - object returned by the ``factory`` associated with this - route). - - The syntax of the ``traverse`` argument is the same as it is - for ``pattern``. For example, if the ``pattern`` provided to - ``add_route`` is ``articles/{article}/edit``, and the - ``traverse`` argument provided to ``add_route`` is - ``/{article}``, when a request comes in that causes the route - to match in such a way that the ``article`` match value is - '1' (when the request URI is ``/articles/1/edit``), the - traversal path will be generated as ``/1``. This means that - the root object's ``__getitem__`` will be called with the - name ``1`` during the traversal phase. If the ``1`` object - exists, it will become the :term:`context` of the request. - :ref:`traversal_chapter` has more information about - traversal. - - If the traversal path contains segment marker names which - are not present in the ``pattern`` argument, a runtime error - will occur. The ``traverse`` pattern should not contain - segment markers that do not exist in the ``pattern`` - argument. - - A similar combining of routing and traversal is available - when a route is matched which contains a ``*traverse`` - remainder marker in its pattern (see - :ref:`using_traverse_in_a_route_pattern`). The ``traverse`` - argument to add_route allows you to associate route patterns - with an arbitrary traversal path without using a a - ``*traverse`` remainder marker; instead you can use other - match information. - - Note that the ``traverse`` argument to ``add_route`` is - ignored when attached to a route that has a ``*traverse`` - remainder marker in its pattern. - - pregenerator - - This option should be a callable object that implements the - :class:`pyramid.interfaces.IRoutePregenerator` - interface. A :term:`pregenerator` is a callable called by - the :mod:`pyramid.url.route_url` function to augment or - replace the arguments it is passed when generating a URL - for the route. This is a feature not often used directly - by applications, it is meant to be hooked by frameworks - that use :app:`Pyramid` as a base. - - Predicate Arguments - - pattern - - The pattern of the route e.g. ``ideas/{idea}``. This - argument is required. See :ref:`route_path_pattern_syntax` - for information about the syntax of route patterns. If the - pattern doesn't match the current URL, route matching - continues. - - .. note:: For backwards compatibility purposes (as of - :app:`Pyramid` 1.0), a ``path`` keyword argument passed - to this function will be used to represent the pattern - value if the ``pattern`` argument is ``None``. If both - ``path`` and ``pattern`` are passed, ``pattern`` wins. - - xhr - - This value should be either ``True`` or ``False``. If this - value is specified and is ``True``, the :term:`request` must - possess an ``HTTP_X_REQUESTED_WITH`` (aka - ``X-Requested-With``) header for this route to match. This - is useful for detecting AJAX requests issued from jQuery, - Prototype and other Javascript libraries. If this predicate - returns ``False``, route matching continues. - - request_method - - A string representing an HTTP method name, e.g. ``GET``, - ``POST``, ``HEAD``, ``DELETE``, ``PUT``. If this argument - is not specified, this route will match if the request has - *any* request method. If this predicate returns ``False``, - route matching continues. - - path_info - - This value represents a regular expression pattern that will - be tested against the ``PATH_INFO`` WSGI environment - variable. If the regex matches, this predicate will return - ``True``. If this predicate returns ``False``, route - matching continues. - - request_param - - This value can be any string. A view declaration with this - argument ensures that the associated route will only match - when the request has a key in the ``request.params`` - dictionary (an HTTP ``GET`` or ``POST`` variable) that has a - name which matches the supplied value. If the value - supplied as the argument has a ``=`` sign in it, - e.g. ``request_params="foo=123"``, then the key - (``foo``) must both exist in the ``request.params`` dictionary, and - the value must match the right hand side of the expression (``123``) - for the route to "match" the current request. If this predicate - returns ``False``, route matching continues. - - header - - This argument represents an HTTP header name or a header - name/value pair. If the argument contains a ``:`` (colon), - it will be considered a name/value pair - (e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). If - the value contains a colon, the value portion should be a - regular expression. If the value does not contain a colon, - the entire value will be considered to be the header name - (e.g. ``If-Modified-Since``). If the value evaluates to a - header name only without a value, the header specified by - the name must be present in the request for this predicate - to be true. If the value evaluates to a header name/value - pair, the header specified by the name must be present in - the request *and* the regular expression specified as the - value must match the header value. Whether or not the value - represents a header name or a header name/value pair, the - case of the header name is not significant. If this - predicate returns ``False``, route matching continues. - - accept - - This value represents a match query for one or more - mimetypes in the ``Accept`` HTTP request header. If this - value is specified, it must be in one of the following - forms: a mimetype match token in the form ``text/plain``, a - wildcard mimetype match token in the form ``text/*`` or a - match-all wildcard mimetype match token in the form ``*/*``. - If any of the forms matches the ``Accept`` header of the - request, this predicate will be true. If this predicate - returns ``False``, route matching continues. - - custom_predicates - - This value should be a sequence of references to custom - predicate callables. Use custom predicates when no set of - predefined predicates does what you need. Custom predicates - can be combined with predefined predicates as necessary. - Each custom predicate callable should accept two arguments: - ``info`` and ``request`` and should return either ``True`` - or ``False`` after doing arbitrary evaluation of the info - and/or the request. If all custom and non-custom predicate - callables return ``True`` the associated route will be - considered viable for a given request. If any predicate - callable returns ``False``, route matching continues. Note - that the value ``info`` passed to a custom route predicate - is a dictionary containing matching information; see - :ref:`custom_route_predicates` for more information about - ``info``. - - View-Related Arguments - - view - - A Python object or :term:`dotted Python name` to the same - object that will be used as a view callable when this route - matches. e.g. ``mypackage.views.my_view``. - - view_context - - A class or an :term:`interface` or :term:`dotted Python - name` to the same object which the :term:`context` of the - view should match for the view named by the route to be - used. This argument is only useful if the ``view`` - attribute is used. If this attribute is not specified, the - default (``None``) will be used. - - If the ``view`` argument is not provided, this argument has - no effect. - - This attribute can also be spelled as ``for_`` or ``view_for``. - - view_permission - - The permission name required to invoke the view associated - with this route. e.g. ``edit``. (see - :ref:`using_security_with_urldispatch` for more information - about permissions). - - If the ``view`` attribute is not provided, this argument has - no effect. - - This argument can also be spelled as ``permission``. - - view_renderer - - This is either a single string term (e.g. ``json``) or a - string implying a path or :term:`resource specification` - (e.g. ``templates/views.pt``). If the renderer value is a - single term (does not contain a dot ``.``), the specified - term will be used to look up a renderer implementation, and - that renderer implementation will be used to construct a - response from the view return value. If the renderer term - contains a dot (``.``), the specified term will be treated - as a path, and the filename extension of the last element in - the path will be used to look up the renderer - implementation, which will be passed the full path. The - renderer implementation will be used to construct a response - from the view return value. See - :ref:`views_which_use_a_renderer` for more information. - - If the ``view`` argument is not provided, this argument has - no effect. - - This argument can also be spelled as ``renderer``. - - view_attr - - The view machinery defaults to using the ``__call__`` method - of the view callable (or the function itself, if the view - callable is a function) to obtain a response dictionary. - The ``attr`` value allows you to vary the method attribute - used to obtain the response. For example, if your view was - a class, and the class has a method named ``index`` and you - wanted to use this method instead of the class' ``__call__`` - method to return the response, you'd say ``attr="index"`` in - the view configuration for the view. This is - most useful when the view definition is a class. - - 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 - ignored, predicates, ignored = _make_predicates( - xhr=xhr, - request_method=request_method, - path_info=path_info, - request_param=request_param, - header=header, - accept=accept, - traverse=traverse, - custom=custom_predicates + BaseConfigurator.__init__( + self, + registry=registry, + package=package, + settings=settings, + root_factory=root_factory, + authentication_policy=authentication_policy, + authorization_policy=authorization_policy, + renderers=renderers, + debug_logger=debug_logger, + locale_negotiator=locale_negotiator, + request_factory=request_factory, + renderer_globals_factory=renderer_globals_factory, + default_permission=default_permission, + session_factory=session_factory, + autocommit=autocommit, ) - - request_iface = self.registry.queryUtility(IRouteRequest, name=name) - if request_iface is None: - bases = use_global_views and (IRequest,) or () - request_iface = route_request_iface(name, bases) - self.registry.registerUtility( - request_iface, IRouteRequest, name=name) - deferred_views = getattr(self.registry, 'deferred_route_views', {}) - view_info = deferred_views.pop(name, ()) - for info in view_info: - self.add_view(**info) - - if view: - if view_context is None: - view_context = view_for - if view_context is None: - view_context = for_ - view_permission = view_permission or permission - view_renderer = view_renderer or renderer - self.add_view( - permission=view_permission, - context=view_context, - view=view, - name='', - route_name=name, - renderer=view_renderer, - attr=view_attr, - _info=_info, - ) - - mapper = self.get_routes_mapper() - - factory = self.maybe_dotted(factory) - if pattern is None: - pattern = path - if pattern is None: - raise ConfigurationError('"pattern" argument may not be None') - - return mapper.connect(name, pattern, factory, predicates=predicates, - pregenerator=pregenerator) - - def get_routes_mapper(self): - """ Return the :term:`routes mapper` object associated with - this configurator's :term:`registry`.""" - mapper = self.registry.queryUtility(IRoutesMapper) - if mapper is None: - mapper = RoutesMapper() - self.registry.registerUtility(mapper, IRoutesMapper) - return mapper - - def scan(self, package=None, categories=None, _info=u''): - """ Scan a Python package and any of its subpackages for - objects marked with :term:`configuration decoration` such as - :class:`pyramid.view.view_config`. Any decorated object found - will influence the current configuration state. - - The ``package`` argument should be a Python :term:`package` or - module object (or a :term:`dotted Python name` which refers to - such a package or module). If ``package`` is ``None``, the - package of the *caller* is used. - - The ``categories`` argument, if provided, should be the - :term:`Venusian` 'scan categories' to use during scanning. - Providing this argument is not often necessary; specifying - scan categories is an extremely advanced usage. - - By default, ``categories`` is ``None`` which will execute - *all* Venusian decorator callbacks including - :app:`Pyramid`-related decorators such as - :class:`pyramid.view.view_config`. If this is not desirable - because the codebase has other Venusian-using decorators that - aren't meant to be invoked during a particular scan, use - ``('pyramid',)`` as a ``categories`` value to limit the execution - of decorator callbacks to only those registered by - :app:`Pyramid` itself. Or pass a sequence of Venusian scan - categories as necessary (e.g. ``('pyramid', 'myframework')``) to - limit the decorators called to the set of categories required. - """ - package = self.maybe_dotted(package) - if package is None: # pragma: no cover - package = caller_package() - - scanner = self.venusian.Scanner(config=self) - scanner.scan(package, categories=categories) - - def add_renderer(self, name, factory, _info=u''): - """ - Add a :app:`Pyramid` :term:`renderer` factory to the - current configuration state. - - The ``name`` argument is the renderer name. Use ``None`` to - represent the default renderer (a renderer which will be used for all - views unless they name another renderer specifically). - - The ``factory`` argument is Python reference to an - implementation of a :term:`renderer` factory or a - :term:`dotted Python name` to same. - - Note that this function must be called *before* any - ``add_view`` invocation that names the renderer name as an - argument. As a result, it's usually a better idea to pass - globally used renderers into the ``Configurator`` constructor - in the sequence of renderers passed as ``renderer`` than it is - to use this method. - """ - factory = self.maybe_dotted(factory) - # if name is None or the empty string, we're trying to register - # a default renderer, but registerUtility is too dumb to accept None - # as a name - if not name: - name = '' - self.registry.registerUtility( - factory, IRendererFactory, name=name, info=_info) - - def override_resource(self, to_override, override_with, - _info=u'', _override=None,): - """ Add a :app:`Pyramid` resource override to the current - configuration state. - - ``to_override`` is a :term:`resource specification` to the - resource being overridden. - - ``override_with`` is a :term:`resource specification` to the - resource that is performing the override. - - See :ref:`resources_chapter` for more - information about resource overrides.""" - if to_override == override_with: - raise ConfigurationError('You cannot override a resource with ' - 'itself') - - package = to_override - path = '' - if ':' in to_override: - package, path = to_override.split(':', 1) - - override_package = override_with - override_prefix = '' - 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)') - - 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)') - - __import__(package) - __import__(override_package) - package = sys.modules[package] - override_package = sys.modules[override_package] - - override = _override or self._override # test jig - override(package, path, override_package, override_prefix, - _info=_info) - - def set_forbidden_view(self, view=None, attr=None, renderer=None, - wrapper=None, _info=u''): - """ Add a default forbidden view to the current configuration - state. - - .. warning:: This method has been deprecated in :app:`Pyramid` - 1.0. *Do not use it for new development; it should only be - used to support older code bases which depend upon it.* See - :ref:`changing_the_forbidden_view` to see how a forbidden - view should be registered in new projects. - - The ``view`` argument should be a :term:`view callable` or a - :term:`dotted Python name` which refers to a view callable. - - The ``attr`` argument should be the attribute of the view - callable used to retrieve the response (see the ``add_view`` - method's ``attr`` argument for a description). - - The ``renderer`` argument should be the name of (or path to) a - :term:`renderer` used to generate a response for this view - (see the - :meth:`pyramid.configuration.Configurator.add_view` - method's ``renderer`` argument for information about how a - configurator relates to a renderer). - - The ``wrapper`` argument should be the name of another view - which will wrap this view when rendered (see the ``add_view`` - method's ``wrapper`` argument for a description).""" - if renderer is not None and not isinstance(renderer, dict): - renderer = {'name':renderer, 'package':self.package} - view = self._derive_view(view, attr=attr, renderer=renderer) - def bwcompat_view(context, request): - context = getattr(request, 'context', None) - return view(context, request) - return self.add_view(bwcompat_view, context=Forbidden, - wrapper=wrapper, _info=_info) - - def set_notfound_view(self, view=None, attr=None, renderer=None, - wrapper=None, _info=u''): - """ Add a default not found view to the current configuration - state. - - .. warning:: This method has been deprecated in - :app:`Pyramid` 1.0. *Do not use it for new development; - it should only be used to support older code bases which - depend upon it.* See :ref:`changing_the_notfound_view` to - see how a not found view should be registered in new - projects. - - The ``view`` argument should be a :term:`view callable` or a - :term:`dotted Python name` which refers to a view callable. - - The ``attr`` argument should be the attribute of the view - callable used to retrieve the response (see the ``add_view`` - method's ``attr`` argument for a description). - - The ``renderer`` argument should be the name of (or path to) a - :term:`renderer` used to generate a response for this view - (see the - :meth:`pyramid.configuration.Configurator.add_view` - method's ``renderer`` argument for information about how a - configurator relates to a renderer). - - The ``wrapper`` argument should be the name of another view - which will wrap this view when rendered (see the ``add_view`` - method's ``wrapper`` argument for a description). - """ - if renderer is not None and not isinstance(renderer, dict): - renderer = {'name':renderer, 'package':self.package} - view = self._derive_view(view, attr=attr, renderer=renderer) - def bwcompat_view(context, request): - context = getattr(request, 'context', None) - return view(context, request) - return self.add_view(bwcompat_view, context=NotFound, - wrapper=wrapper, _info=_info) - - def set_request_factory(self, factory): - """ The object passed as ``factory`` should be an object (or a - :term:`dotted Python name` which refers to an object) which - will be used by the :app:`Pyramid` router to create all - request objects. This factory object must have the same - methods and attributes as the - :class:`pyramid.request.Request` class (particularly - ``__call__``, and ``blank``). - - .. note:: Using the :meth:``request_factory`` argument to the - :class:`pyramid.configuration.Configurator` constructor - can be used to achieve the same purpose. - """ - factory = self.maybe_dotted(factory) - self.registry.registerUtility(factory, IRequestFactory) - - def set_renderer_globals_factory(self, factory): - """ The object passed as ``factory`` should be an callable (or - a :term:`dotted Python name` which refers to an callable) that - will be used by the :app:`Pyramid` rendering machinery as a - renderers global factory (see :ref:`adding_renderer_globals`). - - The ``factory`` callable must accept a single argument named - ``system`` (which will be a dictionary) and it must return a - dictionary. When an application uses a renderer, the - factory's return dictionary will be merged into the ``system`` - dictionary, and therefore will be made available to the code - which uses the renderer. - - .. note:: Using the :meth:`renderer_globals_factory` - argument to the - :class:`pyramid.configuration.Configurator` constructor - can be used to achieve the same purpose. - """ - factory = self.maybe_dotted(factory) - self.registry.registerUtility(factory, IRendererGlobalsFactory) - - def set_locale_negotiator(self, negotiator): - """ - Set the :term:`locale negotiator` for this application. The - :term:`locale negotiator` is a callable which accepts a - :term:`request` object and which returns a :term:`locale - name`. The ``negotiator`` argument should be the locale - negotiator implementation or a :term:`dotted Python name` - which refers to such an implementation. - - Later calls to this method override earlier calls; there can - be only one locale negotiator active at a time within an - application. See :ref:`activating_translation` for more - information. - - .. note:: Using the ``locale_negotiator`` argument to the - :class:`pyramid.configuration.Configurator` constructor - can be used to achieve the same purpose. - """ - negotiator = self.maybe_dotted(negotiator) - self.registry.registerUtility(negotiator, ILocaleNegotiator) - - def set_default_permission(self, permission): - """ - Set the default permission to be used by all subsequent - :term:`view configuration` registrations. ``permission`` - should be a :term:`permission` string to be used as the - default permission. An example of a permission - string:``'view'``. Adding a default permission makes it - unnecessary to protect each view configuration with an - explicit permission, unless your application policy requires - some exception for a particular view. - - If a default permission is *not* set, views represented by - view configuration registrations which do not explicitly - declare a permission will be executable by entirely anonymous - users (any authorization policy is ignored). - - Later calls to this method override earlier calls; there can - be only one default permission active at a time within an - application. - - See also :ref:`setting_a_default_permission`. - - .. note:: Using the ``default_permission`` argument to the - :class:`pyramid.configuration.Configurator` constructor - can be used to achieve the same purpose. - """ - self.registry.registerUtility(permission, IDefaultPermission) - - def set_session_factory(self, session_factory): - """ - Configure the application with a :term:`session factory`. If - this method is called, the ``session_factory`` argument must - be a session factory callable. - """ - self.registry.registerUtility(session_factory, ISessionFactory) - - def add_translation_dirs(self, *specs): - """ Add one or more :term:`translation directory` paths to the - current configuration state. The ``specs`` argument is a - sequence that may contain absolute directory paths - (e.g. ``/usr/share/locale``) or :term:`resource specification` - names naming a directory path (e.g. ``some.package:locale``) - or a combination of the two. - - Example: - - .. code-block:: python - - add_translations_dirs('/usr/share/locale', 'some.package:locale') - - """ - for spec in specs: - - package_name, filename = self._split_spec(spec) - if package_name is None: # absolute filename - directory = filename - else: - __import__(package_name) - package = sys.modules[package_name] - directory = os.path.join(package_path(package), filename) - - if not os.path.isdir(os.path.realpath(directory)): - raise ConfigurationError('"%s" is not a directory' % directory) - - tdirs = self.registry.queryUtility(ITranslationDirectories) - if tdirs is None: - tdirs = [] - self.registry.registerUtility(tdirs, ITranslationDirectories) - - tdirs.insert(0, directory) - - if specs: - - # We actually only need an IChameleonTranslate function - # utility to be registered zero or one times. We register the - # same function once for each added translation directory, - # which does too much work, but has the same effect. - - def translator(msg): - request = get_current_request() - localizer = get_localizer(request) - return localizer.translate(msg) - - ctranslate = ChameleonTranslate(translator) - self.registry.registerUtility(ctranslate, IChameleonTranslate) - - def add_static_view(self, name, path, **kw): - """ Add a view used to render static resources such as images - and CSS files. - - The ``name`` argument is a string representing :term:`view - name` of the view which is registered. It may alternately be - a *url prefix*. - - The ``path`` argument is the path on disk where the static - files reside. This can be an absolute path, a - package-relative path, or a :term:`resource specification`. - - The ``cache_max_age`` keyword argument is input to set the - ``Expires`` and ``Cache-Control`` headers for static resources - served. Note that this argument has no effect when the - ``name`` is a *url prefix*. By default, this argument is - ``None``, meaning that no particular Expires or Cache-Control - headers are set in the response. - - The ``permission`` keyword argument is used to specify the - :term:`permission` required by a user to execute the static - view. By default, it is the string - ``__no_permission_required__``. The - ``__no_permission_required__`` string is a special sentinel - which indicates that, even if a :term:`default permission` - exists for the current application, the static view should be - renderered to completely anonymous users. This default value - is permissive because, in most web apps, static resources - seldom need protection from viewing. - - *Usage* - - The ``add_static_view`` function is typically used in - conjunction with the :func:`pyramid.url.static_url` - function. ``add_static_view`` adds a view which renders a - static resource when some URL is visited; - :func:`pyramid.url.static_url` generates a URL to that - resource. - - The ``name`` argument to ``add_static_view`` is usually a - :term:`view name`. When this is the case, the - :func:`pyramid.url.static_url` API will generate a URL - which points to a Pyramid view, which will serve up a set of - resources that live in the package itself. For example: - - .. code-block:: python - - add_static_view('images', 'mypackage:images/') - - Code that registers such a view can generate URLs to the view - via :func:`pyramid.url.static_url`: - - .. code-block:: python - - static_url('mypackage:images/logo.png', request) - - When ``add_static_view`` is called with a ``name`` argument - that represents a simple view name, as it is above, subsequent - calls to :func:`pyramid.url.static_url` with paths that - start with the ``path`` argument passed to ``add_static_view`` - will generate a URL something like ``http://<Pyramid app - URL>/images/logo.png``, which will cause the ``logo.png`` file - in the ``images`` subdirectory of the ``mypackage`` package to - be served. - - ``add_static_view`` can alternately be used with a ``name`` - argument which is a *URL*, causing static resources to be - served from an external webserver. This happens when the - ``name`` argument is a URL (detected as any string with a - slash in it). In this mode, the ``name`` is used as the URL - prefix when generating a URL using - :func:`pyramid.url.static_url`. For example, if - ``add_static_view`` is called like so: - - .. code-block:: python - - add_static_view('http://example.com/images', 'mypackage:images/') - - Subsequently, the URLs generated by - :func:`pyramid.url.static_url` for that static view will be - prefixed with ``http://example.com/images``: - - .. code-block:: python - - static_url('mypackage:images/logo.png', request) - - When ``add_static_view`` is called with a ``name`` argument - that is the URL prefix ``http://example.com/images``, - subsequent calls to :func:`pyramid.url.static_url` with - paths that start with the ``path`` argument passed to - ``add_static_view`` will generate a URL something like - ``http://example.com/logo.png``. The external webserver - listening on ``example.com`` must be itself configured to - respond properly to such a request. - - See :ref:`static_resources_section` for more information. - """ - spec = self._make_spec(path) - info = self.registry.queryUtility(IStaticURLInfo) - if info is None: - info = StaticURLInfo(self) - self.registry.registerUtility(info, IStaticURLInfo) - - info.add(name, spec, **kw) - - # testing API - def testing_securitypolicy(self, userid=None, groupids=(), - permissive=True): - """Unit/integration testing helper: Registers a pair of faux - :app:`Pyramid` security policies: a :term:`authentication - policy` and a :term:`authorization policy`. - - The behavior of the registered :term:`authorization policy` - depends on the ``permissive`` argument. If ``permissive`` is - true, a permissive :term:`authorization policy` is registered; - this policy allows all access. If ``permissive`` is false, a - nonpermissive :term:`authorization policy` is registered; this - policy denies all access. - - The behavior of the registered :term:`authentication policy` - depends on the values provided for the ``userid`` and - ``groupids`` argument. The authentication policy will return - the userid identifier implied by the ``userid`` argument and - the group ids implied by the ``groupids`` argument when the - :func:`pyramid.security.authenticated_userid` or - :func:`pyramid.security.effective_principals` APIs are - used. - - This function is most useful when testing code that uses - the APIs named :func:`pyramid.security.has_permission`, - :func:`pyramid.security.authenticated_userid`, - :func:`pyramid.security.effective_principals`, and - :func:`pyramid.security.principals_allowed_by_permission`. - """ - from pyramid.testing import DummySecurityPolicy - policy = DummySecurityPolicy(userid, groupids, permissive) - self.registry.registerUtility(policy, IAuthorizationPolicy) - self.registry.registerUtility(policy, IAuthenticationPolicy) - - def testing_models(self, models): - """Unit/integration testing helper: registers a dictionary of - :term:`model` objects that can be resolved via the - :func:`pyramid.traversal.find_model` API. - - The :func:`pyramid.traversal.find_model` API is called with - a path as one of its arguments. If the dictionary you - register when calling this method contains that path as a - string key (e.g. ``/foo/bar`` or ``foo/bar``), the - corresponding value will be returned to ``find_model`` (and - thus to your code) when - :func:`pyramid.traversal.find_model` is called with an - equivalent path string or tuple. - """ - class DummyTraverserFactory: - def __init__(self, context): - self.context = context - - def __call__(self, request): - path = request['PATH_INFO'] - ob = models[path] - traversed = traversal_path(path) - return {'context':ob, 'view_name':'','subpath':(), - 'traversed':traversed, 'virtual_root':ob, - 'virtual_root_path':(), 'root':ob} - self.registry.registerAdapter(DummyTraverserFactory, (Interface,), - ITraverser) - return models - - def testing_add_subscriber(self, event_iface=None): - """Unit/integration testing helper: Registers a - :term:`subscriber` which listens for events of the type - ``event_iface``. This method returns a list object which is - appended to by the subscriber whenever an event is captured. - - When an event is dispatched that matches the value implied by - the ``event_iface`` argument, that event will be appended to - the list. You can then compare the values in the list to - expected event notifications. This method is useful when - testing code that wants to call - :meth:`pyramid.registry.Registry.notify`, - :func:`zope.component.event.dispatch` or - :func:`zope.component.event.objectEventNotify`. - - The default value of ``event_iface`` (``None``) implies a - subscriber registered for *any* kind of event. - """ - event_iface = self.maybe_dotted(event_iface) - L = [] - def subscriber(*event): - L.extend(event) - self.add_subscriber(subscriber, event_iface) - return L - - def testing_add_renderer(self, path, renderer=None): - """Unit/integration testing helper: register a renderer at - ``path`` (usually a relative filename ala ``templates/foo.pt`` - or a resource specification) and return the renderer object. - If the ``renderer`` argument is None, a 'dummy' renderer will - be used. This function is useful when testing code that calls - the :func:`pyramid.renderers.render` function or - :func:`pyramid.renderers.render_to_response` function or - any other ``render_*`` or ``get_*`` API of the - :mod:`pyramid.renderers` module. - - Note that calling this method for with a ``path`` argument - representing a renderer factory type (e.g. for ``foo.pt`` - usually implies the ``chameleon_zpt`` renderer factory) - clobbers any existing renderer factory registered for that - type. - - .. note:: This method is also available under the alias - ``testing_add_template`` (an older name for it). - - """ - from pyramid.testing import DummyRendererFactory - helper = RendererHelper(name=path, registry=self.registry) - factory = self.registry.queryUtility(IRendererFactory, name=helper.type) - if not isinstance(factory, DummyRendererFactory): - factory = DummyRendererFactory(helper.type, factory) - self.registry.registerUtility(factory, IRendererFactory, - name=helper.type) - - from pyramid.testing import DummyTemplateRenderer - if renderer is None: - renderer = DummyTemplateRenderer() - factory.add(path, renderer) - return renderer - - testing_add_template = testing_add_renderer - -def _make_predicates(xhr=None, request_method=None, path_info=None, - request_param=None, header=None, accept=None, - containment=None, request_type=None, - traverse=None, custom=()): - - # PREDICATES - # ---------- - # - # Given an argument list, a predicate list is computed. - # Predicates are added to a predicate list in (presumed) - # computation expense order. All predicates associated with a - # view or route must evaluate true for the view or route to - # "match" during a request. Elsewhere in the code, we evaluate - # predicates using a generator expression. The fastest predicate - # should be evaluated first, then the next fastest, and so on, as - # if one returns false, the remainder of the predicates won't need - # to be evaluated. - # - # While we compute predicates, we also compute a predicate hash - # (aka phash) that can be used by a caller to identify identical - # predicate lists. - # - # ORDERING - # -------- - # - # A "order" is computed for the predicate list. An order is - # a scoring. - # - # Each predicate is associated with a weight value, which is a - # multiple of 2. The weight of a predicate symbolizes the - # relative potential "importance" of the predicate to all other - # predicates. A larger weight indicates greater importance. - # - # All weights for a given predicate list are bitwise ORed together - # to create a "score"; this score is then subtracted from - # MAX_ORDER and divided by an integer representing the number of - # predicates+1 to determine the order. - # - # The order represents the ordering in which a "multiview" ( a - # collection of views that share the same context/request/name - # triad but differ in other ways via predicates) will attempt to - # call its set of views. Views with lower orders will be tried - # first. The intent is to a) ensure that views with more - # predicates are always evaluated before views with fewer - # predicates and b) to ensure a stable call ordering of views that - # share the same number of predicates. Views which do not have - # any predicates get an order of MAX_ORDER, meaning that they will - # be tried very last. - - predicates = [] - weights = [] - h = md5() - - if xhr: - def xhr_predicate(context, request): - return request.is_xhr - weights.append(1 << 1) - predicates.append(xhr_predicate) - h.update('xhr:%r' % bool(xhr)) - - if request_method is not None: - def request_method_predicate(context, request): - return request.method == request_method - weights.append(1 << 2) - predicates.append(request_method_predicate) - h.update('request_method:%r' % request_method) - - if path_info is not None: - try: - path_info_val = re.compile(path_info) - except re.error, why: - raise ConfigurationError(why[0]) - def path_info_predicate(context, request): - return path_info_val.match(request.path_info) is not None - weights.append(1 << 3) - predicates.append(path_info_predicate) - h.update('path_info:%r' % path_info) - - if request_param is not None: - request_param_val = None - if '=' in request_param: - request_param, request_param_val = request_param.split('=', 1) - def request_param_predicate(context, request): - if request_param_val is None: - return request_param in request.params - return request.params.get(request_param) == request_param_val - weights.append(1 << 4) - predicates.append(request_param_predicate) - h.update('request_param:%r=%r' % (request_param, request_param_val)) - - if header is not None: - header_name = header - header_val = None - if ':' in header: - header_name, header_val = header.split(':', 1) - try: - header_val = re.compile(header_val) - except re.error, why: - raise ConfigurationError(why[0]) - def header_predicate(context, request): - if header_val is None: - return header_name in request.headers - val = request.headers.get(header_name) - if val is None: - return False - return header_val.match(val) is not None - weights.append(1 << 5) - predicates.append(header_predicate) - h.update('header:%r=%r' % (header_name, header_val)) - - if accept is not None: - def accept_predicate(context, request): - return accept in request.accept - weights.append(1 << 6) - predicates.append(accept_predicate) - h.update('accept:%r' % accept) - - if containment is not None: - def containment_predicate(context, request): - return find_interface(context, containment) is not None - weights.append(1 << 7) - predicates.append(containment_predicate) - h.update('containment:%r' % hash(containment)) - - if request_type is not None: - def request_type_predicate(context, request): - return request_type.providedBy(request) - weights.append(1 << 8) - predicates.append(request_type_predicate) - h.update('request_type:%r' % hash(request_type)) - - if traverse is not None: - # ``traverse`` can only be used as a *route* "predicate"; it - # adds 'traverse' to the matchdict if it's specified in the - # routing args. This causes the ModelGraphTraverser to use - # the resolved traverse pattern as the traversal path. - from pyramid.urldispatch import _compile_route - _, tgenerate = _compile_route(traverse) - def traverse_predicate(context, request): - if 'traverse' in context: - return True - m = context['match'] - tvalue = tgenerate(m) - m['traverse'] = traversal_path(tvalue) - return True - # This isn't actually a predicate, it's just a infodict - # modifier that injects ``traverse`` into the matchdict. As a - # result, the ``traverse_predicate`` function above always - # returns True, and we don't need to update the hash or attach - # a weight to it - predicates.append(traverse_predicate) - - if custom: - for num, predicate in enumerate(custom): - predicates.append(predicate) - # using hash() here rather than id() is intentional: we - # want to allow custom predicates that are part of - # frameworks to be able to define custom __hash__ - # functions for custom predicates, so that the hash output - # of predicate instances which are "logically the same" - # may compare equal. - h.update('custom%s:%r' % (num, hash(predicate))) - weights.append(1 << 10) - - score = 0 - for bit in weights: - score = score | bit - order = (MAX_ORDER - score) / (len(predicates) + 1) - phash = h.hexdigest() - return order, predicates, phash - -class MultiView(object): - implements(IMultiView) - - def __init__(self, name): - self.name = name - self.media_views = {} - self.views = [] - self.accepts = [] - - def add(self, view, order, accept=None, phash=None): - if phash is not None: - for i, (s, v, h) in enumerate(list(self.views)): - if phash == h: - self.views[i] = (order, view, phash) - return - - if accept is None or '*' in accept: - self.views.append((order, view, phash)) - self.views.sort() - else: - subset = self.media_views.setdefault(accept, []) - subset.append((order, view, phash)) - subset.sort() - accepts = set(self.accepts) - accepts.add(accept) - self.accepts = list(accepts) # dedupe - - def get_views(self, request): - if self.accepts and hasattr(request, 'accept'): - accepts = self.accepts[:] - views = [] - while accepts: - match = request.accept.best_match(accepts) - if match is None: - break - subset = self.media_views[match] - views.extend(subset) - accepts.remove(match) - views.extend(self.views) - return views - return self.views - - def match(self, context, request): - for order, view, phash in self.get_views(request): - if not hasattr(view, '__predicated__'): - return view - if view.__predicated__(context, request): - return view - raise PredicateMismatch(self.name) - - def __permitted__(self, context, request): - view = self.match(context, request) - if hasattr(view, '__permitted__'): - return view.__permitted__(context, request) - return True - - def __call_permissive__(self, context, request): - view = self.match(context, request) - view = getattr(view, '__call_permissive__', view) - return view(context, request) - - def __call__(self, context, request): - for order, view, phash in self.get_views(request): - try: - return view(context, request) - except PredicateMismatch: - continue - raise PredicateMismatch(self.name) - -def decorate_view(wrapped_view, original_view): - if wrapped_view is original_view: - return False - wrapped_view.__module__ = original_view.__module__ - wrapped_view.__doc__ = original_view.__doc__ - try: - wrapped_view.__name__ = original_view.__name__ - except AttributeError: - wrapped_view.__name__ = repr(original_view) - try: - wrapped_view.__permitted__ = original_view.__permitted__ - except AttributeError: - pass - try: - wrapped_view.__call_permissive__ = original_view.__call_permissive__ - except AttributeError: - pass - try: - wrapped_view.__predicated__ = original_view.__predicated__ - except AttributeError: - pass - try: - wrapped_view.__accept__ = original_view.__accept__ - except AttributeError: - pass - try: - wrapped_view.__order__ = original_view.__order__ - except AttributeError: - pass - return True - -def requestonly(class_or_callable, attr=None): - """ Return true of the class or callable accepts only a request argument, - as opposed to something that accepts context, request """ - if attr is None: - attr = '__call__' - if inspect.isfunction(class_or_callable): - fn = class_or_callable - elif inspect.isclass(class_or_callable): - try: - fn = class_or_callable.__init__ - except AttributeError: - return False - else: - try: - fn = getattr(class_or_callable, attr) - except AttributeError: - return False - - try: - argspec = inspect.getargspec(fn) - except TypeError: - return False - - args = argspec[0] - defaults = argspec[3] - - if hasattr(fn, 'im_func'): - # it's an instance method - if not args: - return False - args = args[1:] - if not args: - return False - - if len(args) == 1: - return True - - elif args[0] == 'request': - if len(args) - len(defaults) == 1: - return True - - return False - -def is_response(ob): - if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and - hasattr(ob, 'status') ): - return True - return False - -def _map_view(view, registry, attr=None, renderer=None): - wrapped_view = view - - helper = None - - if renderer is not None: - helper = RendererHelper(renderer['name'], - package=renderer['package'], - registry=registry) - - if inspect.isclass(view): - # If the object we've located is a class, turn it into a - # function that operates like a Zope view (when it's invoked, - # construct an instance using 'context' and 'request' as - # position arguments, then immediately invoke the __call__ - # method of the instance with no arguments; __call__ should - # return an IResponse). - if requestonly(view, attr): - # its __init__ accepts only a single request argument, - # instead of both context and request - def _class_requestonly_view(context, request): - inst = view(request) - if attr is None: - response = inst() - else: - response = getattr(inst, attr)() - if helper is not None: - if not is_response(response): - system = { - 'view':inst, - 'renderer_name':renderer['name'], # b/c - 'renderer_info':renderer, - 'context':context, - 'request':request - } - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _class_requestonly_view - else: - # its __init__ accepts both context and request - def _class_view(context, request): - inst = view(context, request) - if attr is None: - response = inst() - else: - response = getattr(inst, attr)() - if helper is not None: - if not is_response(response): - system = {'view':inst, - 'renderer_name':renderer['name'], # b/c - 'renderer_info':renderer, - 'context':context, - 'request':request - } - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _class_view - - elif requestonly(view, attr): - # its __call__ accepts only a single request argument, - # instead of both context and request - def _requestonly_view(context, request): - if attr is None: - response = view(request) - else: - response = getattr(view, attr)(request) - - if helper is not None: - if not is_response(response): - system = { - 'view':view, - 'renderer_name':renderer['name'], - 'renderer_info':renderer, - 'context':context, - 'request':request - } - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _requestonly_view - - elif attr: - def _attr_view(context, request): - response = getattr(view, attr)(context, request) - if helper is not None: - if not is_response(response): - system = { - 'view':view, - 'renderer_name':renderer['name'], - 'renderer_info':renderer, - 'context':context, - 'request':request - } - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _attr_view - - elif helper is not None: - def _rendered_view(context, request): - response = view(context, request) - if not is_response(response): - system = { - 'view':view, - 'renderer_name':renderer['name'], # b/c - 'renderer_info':renderer, - 'context':context, - 'request':request - } - response = helper.render_to_response(response, system, - request=request) - return response - wrapped_view = _rendered_view - - decorate_view(wrapped_view, view) - return wrapped_view - -def _owrap_view(view, viewname, wrapper_viewname): - if not wrapper_viewname: - return view - def _owrapped_view(context, request): - response = view(context, request) - request.wrapped_response = response - request.wrapped_body = response.body - request.wrapped_view = view - wrapped_response = render_view_to_response(context, request, - wrapper_viewname) - if wrapped_response is None: - raise ValueError( - 'No wrapper view named %r found when executing view ' - 'named %r' % (wrapper_viewname, viewname)) - return wrapped_response - decorate_view(_owrapped_view, view) - return _owrapped_view - -def _predicate_wrap(view, predicates): - if not predicates: - return view - def predicate_wrapper(context, request): - if all((predicate(context, request) for predicate in predicates)): - return view(context, request) - raise PredicateMismatch('predicate mismatch for view %s' % view) - def checker(context, request): - return all((predicate(context, request) for predicate in - predicates)) - predicate_wrapper.__predicated__ = checker - decorate_view(predicate_wrapper, view) - return predicate_wrapper - -def _secure_view(view, permission, authn_policy, authz_policy): - if permission == '__no_permission_required__': - # allow views registered within configurations that have a - # default permission to explicitly override the default - # permission, replacing it with no permission at all - permission = None - - wrapped_view = view - if authn_policy and authz_policy and (permission is not None): - def _secured_view(context, request): - principals = authn_policy.effective_principals(request) - if authz_policy.permits(context, principals, permission): - return view(context, request) - msg = getattr(request, 'authdebug_message', - 'Unauthorized: %s failed permission check' % view) - raise Forbidden(msg) - _secured_view.__call_permissive__ = view - def _permitted(context, request): - principals = authn_policy.effective_principals(request) - return authz_policy.permits(context, principals, permission) - _secured_view.__permitted__ = _permitted - wrapped_view = _secured_view - decorate_view(wrapped_view, view) - - return wrapped_view - -def _authdebug_view(view, permission, authn_policy, authz_policy, settings, - logger): - wrapped_view = view - if settings and settings.get('debug_authorization', False): - def _authdebug_view(context, request): - view_name = getattr(request, 'view_name', None) - - if authn_policy and authz_policy: - if permission is None: - msg = 'Allowed (no permission registered)' - else: - principals = authn_policy.effective_principals(request) - msg = str(authz_policy.permits(context, principals, - permission)) - else: - msg = 'Allowed (no authorization policy in use)' - - view_name = getattr(request, 'view_name', None) - url = getattr(request, 'url', None) - msg = ('debug_authorization of url %s (view name %r against ' - 'context %r): %s' % (url, view_name, context, msg)) - logger and logger.debug(msg) - if request is not None: - request.authdebug_message = msg - return view(context, request) - - wrapped_view = _authdebug_view - decorate_view(wrapped_view, view) - - return wrapped_view - -def _attr_wrap(view, accept, order, phash): - # this is a little silly but we don't want to decorate the original - # function with attributes that indicate accept, order, and phash, - # so we use a wrapper - if (accept is None) and (order == MAX_ORDER) and (phash == DEFAULT_PHASH): - return view # defaults - def attr_view(context, request): - return view(context, request) - attr_view.__accept__ = accept - attr_view.__order__ = order - attr_view.__phash__ = phash - decorate_view(attr_view, view) - return attr_view - -def isexception(o): - if IInterface.providedBy(o): - if IException.isEqualOrExtendedBy(o): - return True - return ( - isinstance(o, Exception) or - (inspect.isclass(o) and (issubclass(o, Exception))) - ) - -# note that ``options`` is a b/w compat alias for ``settings`` and -# ``Configurator`` is a testing dep inj -def make_app(root_factory, package=None, filename='configure.zcml', - settings=None, options=None, Configurator=Configurator): - """ Return a Router object, representing a fully configured - :app:`Pyramid` WSGI application. - - .. warning:: Use of this function is deprecated as of - :app:`Pyramid` 1.0. You should instead use a - :class:`pyramid.configuration.Configurator` instance to - perform startup configuration as shown in - :ref:`configuration_narr`. - - ``root_factory`` must be a callable that accepts a :term:`request` - object and which returns a traversal root object. The traversal - root returned by the root factory is the *default* traversal root; - it can be overridden on a per-view basis. ``root_factory`` may be - ``None``, in which case a 'default default' traversal root is - used. - - ``package`` is a Python :term:`package` or module representing the - application's package. It is optional, defaulting to ``None``. - ``package`` may be ``None``. If ``package`` is ``None``, the - ``filename`` passed or the value in the ``options`` dictionary - named ``configure_zcml`` must be a) absolute pathname to a - :term:`ZCML` file that represents the application's configuration - *or* b) a :term:`resource specification` to a :term:`ZCML` file in - the form ``dotted.package.name:relative/file/path.zcml``. - - ``filename`` is the filesystem path to a ZCML file (optionally - relative to the package path) that should be parsed to create the - application registry. It defaults to ``configure.zcml``. It can - also be a ;term:`resource specification` in the form - ``dotted_package_name:relative/file/path.zcml``. Note that if any - value for ``configure_zcml`` is passed within the ``settings`` - dictionary, the value passed as ``filename`` will be ignored, - replaced with the ``configure_zcml`` value. - - ``settings``, if used, should be a dictionary containing runtime - settings (e.g. the key/value pairs in an app section of a - PasteDeploy file), with each key representing the option and the - key's value representing the specific option value, - e.g. ``{'reload_templates':True}``. Note that the keyword - parameter ``options`` is a backwards compatibility alias for the - ``settings`` keyword parameter. - """ - settings = settings or options or {} - zcml_file = settings.get('configure_zcml', filename) - config = Configurator(package=package, settings=settings, - root_factory=root_factory) - config.hook_zca() - config.begin() - config.load_zcml(zcml_file) - config.end() - return config.make_wsgi_app() - -class ActionPredicate(object): - action_name = 'action' - def __init__(self, action): - self.action = action - try: - self.action_re = re.compile(action + '$') - except (re.error, TypeError), why: - raise ConfigurationError(why[0]) - - def __call__(self, context, request): - matchdict = request.matchdict - if matchdict is None: - return False - action = matchdict.get(self.action_name) - if action is None: - return False - return bool(self.action_re.match(action)) - - def __hash__(self): - # allow this predicate's phash to be compared as equal to - # others that share the same action name - return hash(self.action) + +deprecated( + 'Configurator', + 'pyramid.configuration.Configurator is deprecated as of Pyramid 1.0. Use' + '``pyramid.config.Configurator`` with ``autocommit=True`` instead.') diff --git a/pyramid/events.py b/pyramid/events.py index b896d985f..f7cb7d706 100644 --- a/pyramid/events.py +++ b/pyramid/events.py @@ -51,7 +51,7 @@ class subscriber(object): .. code-block:: python - from pyramid.configuration import Configurator + from pyramid.config import Configurator config = Configurator() config.scan('somepackage_containing_subscribers') @@ -142,7 +142,7 @@ AfterTraversal = ContextFound # b/c as of 1.0 class ApplicationCreated(object): """ An instance of this class is emitted as an :term:`event` when - the :meth:`pyramid.configuration.Configurator.make_wsgi_app` is + the :meth:`pyramid.config.Configurator.make_wsgi_app` is called. The instance has an attribute, ``app``, which is an instance of the :term:`router` that will handle WSGI requests. This class implements the @@ -179,7 +179,7 @@ class BeforeRender(dict): An object of this type is sent as an event just before a :term:`renderer` is invoked (but *after* the application-level renderer globals factory added via - :class:`pyramid.configuration.Configurator.set_renderer_globals_factory`, + :class:`pyramid.config.Configurator.set_renderer_globals_factory`, if any, has injected its own keys into the renderer globals dictionary). If a subscriber attempts to add a key that already exist in the renderer diff --git a/pyramid/includes/meta.zcml b/pyramid/includes/meta.zcml index bd16c3ea6..58af814ef 100644 --- a/pyramid/includes/meta.zcml +++ b/pyramid/includes/meta.zcml @@ -35,6 +35,12 @@ /> <meta:directive + name="handler" + schema="pyramid.zcml.IHandlerDirective" + handler="pyramid.zcml.handler" + /> + + <meta:directive name="resource" schema="pyramid.zcml.IResourceDirective" handler="pyramid.zcml.resource" diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index f021ba60b..94affd2bb 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -33,7 +33,7 @@ class INewResponse(Interface): class IApplicationCreated(Interface): """ Event issued when the - :meth:`pyramid.configuration.Configurator.make_wsgi_app` method + :meth:`pyramid.config.Configurator.make_wsgi_app` method is called. See the documentation attached to :class:`pyramid.events.ApplicationCreated` for more information. @@ -312,7 +312,7 @@ class IRoutePregenerator(Interface): You can employ a pregenerator by passing a ``pregenerator`` argument to the - :meth:`pyramid.configuration.Configurator.add_route` + :meth:`pyramid.config.Configurator.add_route` function. """ diff --git a/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl b/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl index f6591ab32..63d43b052 100755 --- a/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/alchemy/+package+/__init__.py_tmpl @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from sqlalchemy import engine_from_config from {{package}}.models import appmaker diff --git a/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl index 9f3793538..c85cc7518 100644 --- a/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/pylons_basic/+package+/__init__.py_tmpl @@ -1,9 +1,9 @@ from pyramid_beaker import session_factory_from_settings +from pyramid.config import Configurator def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - from pyramid.configuration import Configurator config = Configurator(settings=settings) session_factory = session_factory_from_settings(settings) config.set_session_factory(session_factory) diff --git a/pyramid/paster_templates/pylons_basic/+package+/tests.py_tmpl b/pyramid/paster_templates/pylons_basic/+package+/tests.py_tmpl index 7b2bec33c..9716146e0 100644 --- a/pyramid/paster_templates/pylons_basic/+package+/tests.py_tmpl +++ b/pyramid/paster_templates/pylons_basic/+package+/tests.py_tmpl @@ -1,10 +1,10 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator class HelloHandlerTests(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl index 5e284ec88..21512a897 100644 --- a/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/pylons_minimal/+package+/__init__.py_tmpl @@ -1,9 +1,9 @@ from pyramid_beaker import session_factory_from_settings +from pyramid.config import Configurator def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - from pyramid.configuration import Configurator config = Configurator(settings=settings) session_factory = session_factory_from_settings(settings) config.set_session_factory(session_factory) diff --git a/pyramid/paster_templates/pylons_minimal/+package+/tests.py_tmpl b/pyramid/paster_templates/pylons_minimal/+package+/tests.py_tmpl index d98bee355..5f6c0da65 100644 --- a/pyramid/paster_templates/pylons_minimal/+package+/tests.py_tmpl +++ b/pyramid/paster_templates/pylons_minimal/+package+/tests.py_tmpl @@ -1,10 +1,10 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator class MyControllerTests(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl b/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl index b67a2c35b..d403ba29d 100644 --- a/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/pylons_sqla/+package+/__init__.py_tmpl @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid_beaker import session_factory_from_settings diff --git a/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl b/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl index b55760b37..f2f95da60 100644 --- a/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl +++ b/pyramid/paster_templates/pylons_sqla/+package+/tests.py_tmpl @@ -2,11 +2,11 @@ import unittest class MyHandlerTests(unittest.TestCase): def setUp(self): - from pyramid.configuration import Configurator + from pyramid.config import Configurator from sqlalchemy import create_engine from {{package}}.models import initialize_sql self.session = initialize_sql(create_engine('sqlite://')) - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl index 5c326caa8..479740297 100644 --- a/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/routesalchemy/+package+/__init__.py_tmpl @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from sqlalchemy import engine_from_config from {{package}}.models import initialize_sql diff --git a/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl b/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl index cfab37670..74ab8e08a 100644 --- a/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl +++ b/pyramid/paster_templates/routesalchemy/+package+/tests.py_tmpl @@ -1,5 +1,5 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing def _initTestingDB(): @@ -10,7 +10,7 @@ def _initTestingDB(): class TestMyView(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() _initTestingDB() diff --git a/pyramid/paster_templates/starter/+package+/__init__.py_tmpl b/pyramid/paster_templates/starter/+package+/__init__.py_tmpl index 85cb7ec6a..2cc9c17e0 100644 --- a/pyramid/paster_templates/starter/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/starter/+package+/__init__.py_tmpl @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from {{package}}.models import get_root def main(global_config, **settings): diff --git a/pyramid/paster_templates/starter/+package+/tests.py_tmpl b/pyramid/paster_templates/starter/+package+/tests.py_tmpl index 2b84bee58..e1b795b7c 100644 --- a/pyramid/paster_templates/starter/+package+/tests.py_tmpl +++ b/pyramid/paster_templates/starter/+package+/tests.py_tmpl @@ -1,11 +1,11 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing class ViewTests(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl b/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl index 3a0c6b7de..56d201f19 100644 --- a/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/starter_zcml/+package+/__init__.py_tmpl @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from {{package}}.models import get_root def main(global_config, **settings): diff --git a/pyramid/paster_templates/starter_zcml/+package+/tests.py_tmpl b/pyramid/paster_templates/starter_zcml/+package+/tests.py_tmpl index 2b84bee58..e1b795b7c 100644 --- a/pyramid/paster_templates/starter_zcml/+package+/tests.py_tmpl +++ b/pyramid/paster_templates/starter_zcml/+package+/tests.py_tmpl @@ -1,11 +1,11 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing class ViewTests(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl b/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl index 6b05ac7ad..5c8f64f63 100644 --- a/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl +++ b/pyramid/paster_templates/zodb/+package+/__init__.py_tmpl @@ -1,4 +1,4 @@ -from pyramid.configuration import Configurator +from pyramid.config import Configurator from repoze.zodbconn.finder import PersistentApplicationFinder from {{package}}.models import appmaker diff --git a/pyramid/paster_templates/zodb/+package+/tests.py_tmpl b/pyramid/paster_templates/zodb/+package+/tests.py_tmpl index 18041c64c..1d5c76c72 100644 --- a/pyramid/paster_templates/zodb/+package+/tests.py_tmpl +++ b/pyramid/paster_templates/zodb/+package+/tests.py_tmpl @@ -1,11 +1,11 @@ import unittest -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid import testing class ViewTests(unittest.TestCase): def setUp(self): - self.config = Configurator() + self.config = Configurator(autocommit=True) self.config.begin() def tearDown(self): diff --git a/pyramid/registry.py b/pyramid/registry.py index edb3190c5..37e230dc3 100644 --- a/pyramid/registry.py +++ b/pyramid/registry.py @@ -1,4 +1,5 @@ from zope.component.registry import Components + from pyramid.interfaces import ISettings class Registry(Components, dict): diff --git a/pyramid/request.py b/pyramid/request.py index 2f9ca5819..6c9a1e421 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -212,20 +212,20 @@ class Request(WebobRequest): This call to :meth:`pyramid.request.Request.model_url`:: - request.route_url(mymodel) + request.model_url(mymodel) Is completely equivalent to calling :func:`pyramid.url.model_url` like this:: from pyramid.url import model_url - route_url(model, request) + model_url(model, request) """ return model_url(model, self, *elements, **kw) def static_url(self, path, **kw): """ Generates a fully qualified URL for a static :term:`resource`. The resource must live within a location defined via the - :meth:`pyramid.configuration.Configurator.add_static_view` + :meth:`pyramid.config.Configurator.add_static_view` :term:`configuration declaration` or the ``<static>`` ZCML directive (see :ref:`static_resources_section`). diff --git a/pyramid/router.py b/pyramid/router.py index 972c05b62..ca2f0adc5 100644 --- a/pyramid/router.py +++ b/pyramid/router.py @@ -1,6 +1,8 @@ from zope.interface import implements from zope.interface import providedBy +from zope.deprecation import deprecated + from pyramid.interfaces import IDebugLogger from pyramid.interfaces import IExceptionViewClassifier from pyramid.interfaces import IRequest @@ -22,10 +24,7 @@ from pyramid.threadlocal import manager from pyramid.traversal import DefaultRootFactory from pyramid.traversal import ModelGraphTraverser -from pyramid.configuration import make_app # b/c - -make_app # prevent PyFlakes from complaining - +from pyramid.config import Configurator # b/c class Router(object): implements(IRouter) @@ -188,3 +187,65 @@ class Router(object): +# note that ``options`` is a b/w compat alias for ``settings`` and +# ``Configurator`` is a testing dep inj +def make_app(root_factory, package=None, filename='configure.zcml', + settings=None, options=None, Configurator=Configurator): + """ Return a Router object, representing a fully configured + :app:`Pyramid` WSGI application. + + .. warning:: Use of this function is deprecated as of + :app:`Pyramid` 1.0. You should instead use a + :class:`pyramid.config.Configurator` instance to + perform startup configuration as shown in + :ref:`configuration_narr`. + + ``root_factory`` must be a callable that accepts a :term:`request` + object and which returns a traversal root object. The traversal + root returned by the root factory is the *default* traversal root; + it can be overridden on a per-view basis. ``root_factory`` may be + ``None``, in which case a 'default default' traversal root is + used. + + ``package`` is a Python :term:`package` or module representing the + application's package. It is optional, defaulting to ``None``. + ``package`` may be ``None``. If ``package`` is ``None``, the + ``filename`` passed or the value in the ``options`` dictionary + named ``configure_zcml`` must be a) absolute pathname to a + :term:`ZCML` file that represents the application's configuration + *or* b) a :term:`resource specification` to a :term:`ZCML` file in + the form ``dotted.package.name:relative/file/path.zcml``. + + ``filename`` is the filesystem path to a ZCML file (optionally + relative to the package path) that should be parsed to create the + application registry. It defaults to ``configure.zcml``. It can + also be a ;term:`resource specification` in the form + ``dotted_package_name:relative/file/path.zcml``. Note that if any + value for ``configure_zcml`` is passed within the ``settings`` + dictionary, the value passed as ``filename`` will be ignored, + replaced with the ``configure_zcml`` value. + + ``settings``, if used, should be a dictionary containing runtime + settings (e.g. the key/value pairs in an app section of a + PasteDeploy file), with each key representing the option and the + key's value representing the specific option value, + e.g. ``{'reload_templates':True}``. Note that the keyword + parameter ``options`` is a backwards compatibility alias for the + ``settings`` keyword parameter. + """ + settings = settings or options or {} + zcml_file = settings.get('configure_zcml', filename) + config = Configurator(package=package, settings=settings, + root_factory=root_factory, autocommit=True) + config.hook_zca() + config.begin() + config.load_zcml(zcml_file) + config.end() + return config.make_wsgi_app() + + +deprecated( + 'make_app', + 'pyramid.router.make_app is deprecated as of Pyramid 1.0. Use ' + 'an instance of ``pyramid.config.Configurator`` to configure your ' + 'application instead.') diff --git a/pyramid/session.py b/pyramid/session.py index b4bc34a6a..3ea3dbcf8 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -50,9 +50,9 @@ def UnencryptedCookieSessionFactoryConfig( (but signed) cookie-based sessions. The return value of this function is a :term:`session factory`, which may be provided as the ``session_factory`` argument of a - :class:`pyramid.configuration.Configurator` constructor, or used + :class:`pyramid.config.Configurator` constructor, or used as the ``session_factory`` argument of the - :meth:`pyramid.configuration.Configurator.set_session_factory` + :meth:`pyramid.config.Configurator.set_session_factory` method. The session factory returned by this function will create sessions diff --git a/pyramid/settings.py b/pyramid/settings.py index 928dd1915..7cee68926 100644 --- a/pyramid/settings.py +++ b/pyramid/settings.py @@ -67,14 +67,14 @@ def get_settings(): Return a 'settings' object for the current application. A 'settings' object is a dictionary-like object that contains key/value pairs based on the dictionary passed as the ``settings`` - argument to the :class:`pyramid.configuration.Configurator` + argument to the :class:`pyramid.config.Configurator` constructor or the :func:`pyramid.router.make_app` API. .. note:: For backwards compatibility, dictionary keys can also be looked up as attributes of the settings object. .. note:: the - :class:`pyramid.configuration.Configurator.get_settings` method + :class:`pyramid.config.Configurator.get_settings` method performs the same duty. .. warning:: This method is deprecated as of Pyramid 1.0. Use diff --git a/pyramid/static.py b/pyramid/static.py index 5b7c4b199..645e78da1 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -136,7 +136,6 @@ class StaticURLInfo(object): self.registrations.append((name, spec, True)) else: # it's a view name - _info = extra.pop('_info', None) cache_max_age = extra.pop('cache_max_age', None) view = static_view(spec, cache_max_age=cache_max_age) # register a route using this view @@ -148,7 +147,6 @@ class StaticURLInfo(object): view_for=self.__class__, view_permission=permission, factory=lambda *x: self, - _info=_info ) self.registrations.append((name, spec, False)) @@ -183,7 +181,7 @@ class static_view(object): .. note:: If the ``root_dir`` is relative to a :term:`package`, or is a :term:`resource specification` the :app:`Pyramid` ``resource`` ZCML directive or - :class:`pyramid.configuration.Configurator` method can be + :class:`pyramid.config.Configurator` method can be used to override resources within the named ``root_dir`` package-relative directory. However, if the ``root_dir`` is absolute, the ``resource`` directive will not be able to diff --git a/pyramid/testing.py b/pyramid/testing.py index cc0ee5c5b..8d48bfdf4 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -12,7 +12,7 @@ from pyramid.interfaces import ISecuredView from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid.exceptions import Forbidden from pyramid.response import Response from pyramid.registry import Registry @@ -52,13 +52,15 @@ def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True): .. warning:: This API is deprecated as of :app:`Pyramid` 1.0. Instead use the - :meth:`pyramid.configuration.Configurator.testing_securitypolicy` + :meth:`pyramid.config.Configurator.testing_securitypolicy` method in your unit and integration tests. """ registry = get_current_registry() config = Configurator(registry=registry) - return config.testing_securitypolicy(userid=userid, groupids=groupids, - permissive=permissive) + result = config.testing_securitypolicy(userid=userid, groupids=groupids, + permissive=permissive) + config.commit() + return result def registerModels(models): """ Registers a dictionary of :term:`model` objects that can be @@ -74,12 +76,14 @@ def registerModels(models): .. warning:: This API is deprecated as of :app:`Pyramid` 1.0. Instead use the - :meth:`pyramid.configuration.Configurator.testing_models` + :meth:`pyramid.config.Configurator.testing_models` method in your unit and integration tests. """ registry = get_current_registry() config = Configurator(registry=registry) - return config.testing_models(models) + result = config.testing_models(models) + config.commit() + return result def registerEventListener(event_iface=None): """ Registers an :term:`event` listener (aka :term:`subscriber`) @@ -100,12 +104,14 @@ def registerEventListener(event_iface=None): .. warning:: This API is deprecated as of :app:`Pyramid` 1.0. Instead use the - :meth:`pyramid.configuration.Configurator.testing_add_subscriber` + :meth:`pyramid.config.Configurator.testing_add_subscriber` method in your unit and integration tests. """ registry = get_current_registry() config = Configurator(registry=registry) - return config.testing_add_subscriber(event_iface) + result = config.testing_add_subscriber(event_iface) + config.commit() + return result def registerTemplateRenderer(path, renderer=None): """ Register a template renderer at ``path`` (usually a relative @@ -119,13 +125,15 @@ def registerTemplateRenderer(path, renderer=None): .. warning:: This API is deprecated as of :app:`Pyramid` 1.0. Instead use the - :meth:`pyramid.configuration.Configurator.testing_add_template` + :meth:`pyramid.config.Configurator.testing_add_template` method in your unit and integration tests. """ registry = get_current_registry() config = Configurator(registry=registry) - return config.testing_add_template(path, renderer) + result = config.testing_add_template(path, renderer) + config.commit() + return result # registerDummyRenderer is a deprecated alias that should never be removed # (too much usage in the wild) @@ -151,7 +159,7 @@ def registerView(name, result='', view=None, for_=(Interface, Interface), .. warning:: This API is deprecated as of :app:`Pyramid` 1.0. Instead use the - :meth:`pyramid.configuration.Configurator.add_view` + :meth:`pyramid.config.Configurator.add_view` method in your unit and integration tests. """ for_ = (IViewClassifier, ) + for_ @@ -244,12 +252,14 @@ def registerSubscriber(subscriber, iface=Interface): .. warning:: This API is deprecated as of :app:`Pyramid` 1.0. Instead use the - :meth:`pyramid.configuration.Configurator.add_subscriber` + :meth:`pyramid.config.Configurator.add_subscriber` method in your unit and integration tests. """ registry = get_current_registry() config = Configurator(registry) - return config.add_subscriber(subscriber, iface=iface) + result = config.add_subscriber(subscriber, iface=iface) + config.commit() + return result def registerRoute(pattern, name, factory=None): """ Register a new :term:`route` using a pattern @@ -265,12 +275,14 @@ def registerRoute(pattern, name, factory=None): .. warning:: This API is deprecated as of :app:`Pyramid` 1.0. Instead use the - :meth:`pyramid.configuration.Configurator.add_route` + :meth:`pyramid.config.Configurator.add_route` method in your unit and integration tests. """ reg = get_current_registry() config = Configurator(registry=reg) - return config.add_route(name, pattern, factory=factory) + result = config.add_route(name, pattern, factory=factory) + config.commit() + return result def registerSettings(dictarg=None, **kw): """Register one or more 'setting' key/value pairs. A setting is @@ -291,7 +303,7 @@ def registerSettings(dictarg=None, **kw): .. warning:: This API is deprecated as of :app:`Pyramid` 1.0. Instead use the - :meth:`pyramid.configuration.Configurator.add_settings` + :meth:`pyramid.config.Configurator.add_settings` method in your unit and integration tests. """ registry = get_current_registry() @@ -550,7 +562,7 @@ class DummyRequest(object): self.response_callbacks = [] self.response_callbacks.append(callback) -def setUp(registry=None, request=None, hook_zca=True): +def setUp(registry=None, request=None, hook_zca=True, autocommit=True): """ Set :app:`Pyramid` registry and request thread locals for the duration of a single unit test. @@ -561,7 +573,7 @@ def setUp(registry=None, request=None, hook_zca=True): - any of the ``register*`` functions in :mod:`pyramid.testing` (such as :func:`pyramid.testing.registerModels`) - - any method of the :class:`pyramid.configuration.Configurator` + - any method of the :class:`pyramid.config.Configurator` object returned by this function. - the :func:`pyramid.threadlocal.get_current_registry` or @@ -599,7 +611,7 @@ def setUp(registry=None, request=None, hook_zca=True): ``hook_zca`` is ``False``, the hook will not be set. This function returns an instance of the - :class:`pyramid.configuration.Configurator` class, which can be + :class:`pyramid.config.Configurator` class, which can be used for further configuration to set up an environment suitable for a unit or integration test. The ``registry`` attribute attached to the Configurator instance represents the 'current' @@ -618,14 +630,14 @@ def setUp(registry=None, request=None, hook_zca=True): manager.clear() if registry is None: registry = Registry('testing') - config = Configurator(registry=registry) + config = Configurator(registry=registry, autocommit=autocommit) if hasattr(registry, 'registerUtility'): # Sometimes nose calls us with a non-registry object because # it thinks this function is module test setup. Likewise, # someone may be passing us an esoteric "dummy" registry, and # the below won't succeed if it doesn't have a registerUtility # method. - from pyramid.configuration import DEFAULT_RENDERERS + from pyramid.config import DEFAULT_RENDERERS for name, renderer in DEFAULT_RENDERERS: # Cause the default renderers to be registered because # in-the-wild test code relies on being able to call @@ -637,6 +649,7 @@ def setUp(registry=None, request=None, hook_zca=True): # ``render_template`` and friends went behind the back of # any existing renderer factory lookup system. config.add_renderer(name, renderer) + config.commit() hook_zca and config.hook_zca() config.begin(request=request) return config @@ -691,7 +704,7 @@ def cleanUp(*arg, **kw): class DummyRendererFactory(object): """ Registered by - ``pyramid.configuration.Configurator.testing_add_renderer`` as + ``pyramid.config.Configurator.testing_add_renderer`` as a dummy renderer factory. The indecision about what to use as a key (a spec vs. a relative name) is caused by test suites in the wild believing they can register either. The ``factory`` argument diff --git a/pyramid/tests/includeapp1/__init__.py b/pyramid/tests/includeapp1/__init__.py new file mode 100644 index 000000000..eaeeb7ef6 --- /dev/null +++ b/pyramid/tests/includeapp1/__init__.py @@ -0,0 +1 @@ +# include app diff --git a/pyramid/tests/includeapp1/root.py b/pyramid/tests/includeapp1/root.py new file mode 100644 index 000000000..3b042c8c2 --- /dev/null +++ b/pyramid/tests/includeapp1/root.py @@ -0,0 +1,10 @@ +from pyramid.response import Response + +def aview(request): + return Response('root') + +def configure(config): + config.add_view(aview) + config.include('pyramid.tests.includeapp1.two.configure') + config.commit() + diff --git a/pyramid/tests/includeapp1/three.py b/pyramid/tests/includeapp1/three.py new file mode 100644 index 000000000..de7268bb6 --- /dev/null +++ b/pyramid/tests/includeapp1/three.py @@ -0,0 +1,10 @@ +from pyramid.response import Response + +def aview(request): + return Response('three') + +def configure(config): + config.add_view(aview, name='three') + config.include('pyramid.tests.includeapp1.two.configure') # should not cycle + config.add_view(aview) # will be overridden by root when resolved + diff --git a/pyramid/tests/includeapp1/two.py b/pyramid/tests/includeapp1/two.py new file mode 100644 index 000000000..960d77e3d --- /dev/null +++ b/pyramid/tests/includeapp1/two.py @@ -0,0 +1,9 @@ +from pyramid.response import Response + +def aview(request): + return Response('two') + +def configure(config): + config.add_view(aview, name='two') + config.include('pyramid.tests.includeapp1.three.configure') + config.add_view(aview) # will be overridden by root when resolved diff --git a/pyramid/tests/test_chameleon_text.py b/pyramid/tests/test_chameleon_text.py index 9486bbc88..79bc7984c 100644 --- a/pyramid/tests/test_chameleon_text.py +++ b/pyramid/tests/test_chameleon_text.py @@ -2,6 +2,7 @@ import unittest from pyramid.testing import cleanUp from pyramid.testing import skip_on +from pyramid import testing class Base: def setUp(self): @@ -34,10 +35,9 @@ class Base: class TextTemplateRendererTests(Base, unittest.TestCase): def setUp(self): - from pyramid.configuration import Configurator from pyramid.registry import Registry registry = Registry() - self.config = Configurator(registry=registry) + self.config = testing.setUp(registry=registry) self.config.begin() def tearDown(self): diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py index 802f4460f..4601c2f12 100644 --- a/pyramid/tests/test_chameleon_zpt.py +++ b/pyramid/tests/test_chameleon_zpt.py @@ -2,6 +2,7 @@ import unittest from pyramid.testing import cleanUp from pyramid.testing import skip_on +from pyramid import testing class Base(object): def setUp(self): @@ -27,10 +28,9 @@ class Base(object): class ZPTTemplateRendererTests(Base, unittest.TestCase): def setUp(self): - from pyramid.configuration import Configurator from pyramid.registry import Registry registry = Registry() - self.config = Configurator(registry=registry) + self.config = testing.setUp(registry=registry) self.config.begin() def tearDown(self): diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py new file mode 100644 index 000000000..a1e5e28f4 --- /dev/null +++ b/pyramid/tests/test_config.py @@ -0,0 +1,4579 @@ +import unittest + +from pyramid import testing + +try: + import __pypy__ +except: + __pypy__ = None + +class ConfiguratorTests(unittest.TestCase): + def _makeOne(self, *arg, **kw): + from pyramid.config import Configurator + return Configurator(*arg, **kw) + + 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 _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 _getRouteRequestIface(self, config, name): + from pyramid.interfaces import IRouteRequest + iface = config.registry.getUtility(IRouteRequest, name) + return iface + + def _assertNotFound(self, wrapper, *arg): + from pyramid.exceptions import NotFound + self.assertRaises(NotFound, wrapper, *arg) + + def _registerEventListener(self, config, event_iface=None): + if event_iface is None: # pragma: no cover + from zope.interface import Interface + event_iface = Interface + L = [] + def subscriber(*event): + L.extend(event) + config.registry.registerHandler(subscriber, (event_iface,)) + return L + + def _registerLogger(self, config): + from pyramid.interfaces import IDebugLogger + logger = DummyLogger() + config.registry.registerUtility(logger, IDebugLogger) + return logger + + def _makeRequest(self, config): + request = DummyRequest() + request.registry = config.registry + return request + + def _registerSecurityPolicy(self, config, permissive): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + policy = DummySecurityPolicy(permissive) + config.registry.registerUtility(policy, IAuthenticationPolicy) + config.registry.registerUtility(policy, IAuthorizationPolicy) + + def _registerSettings(self, config, **settings): + config.registry.settings = settings + + def test_ctor_no_registry(self): + import sys + from pyramid.interfaces import ISettings + from pyramid.config import Configurator + from pyramid.interfaces import IRendererFactory + config = Configurator() + this_pkg = sys.modules['pyramid.tests'] + self.failUnless(config.registry.getUtility(ISettings)) + self.assertEqual(config.package, this_pkg) + self.failUnless(config.registry.getUtility(IRendererFactory, 'json')) + self.failUnless(config.registry.getUtility(IRendererFactory, 'string')) + if not __pypy__: + self.failUnless(config.registry.getUtility(IRendererFactory, '.pt')) + self.failUnless(config.registry.getUtility(IRendererFactory,'.txt')) + self.failUnless(config.registry.getUtility(IRendererFactory, '.mak')) + self.failUnless(config.registry.getUtility(IRendererFactory, '.mako')) + + def test_begin(self): + from pyramid.config import Configurator + config = Configurator() + manager = DummyThreadLocalManager() + config.manager = manager + config.begin() + self.assertEqual(manager.pushed, + {'registry':config.registry, 'request':None}) + self.assertEqual(manager.popped, False) + + def test_begin_with_request(self): + from pyramid.config import Configurator + config = Configurator() + request = object() + manager = DummyThreadLocalManager() + config.manager = manager + config.begin(request=request) + self.assertEqual(manager.pushed, + {'registry':config.registry, 'request':request}) + self.assertEqual(manager.popped, False) + + def test_end(self): + from pyramid.config import Configurator + config = Configurator() + manager = DummyThreadLocalManager() + config.manager = manager + config.end() + self.assertEqual(manager.pushed, None) + self.assertEqual(manager.popped, True) + + def test_ctor_with_package_registry(self): + import sys + from pyramid.config import Configurator + pkg = sys.modules['pyramid'] + config = Configurator(package=pkg) + self.assertEqual(config.package, pkg) + + def test_ctor_noreg_custom_settings(self): + from pyramid.interfaces import ISettings + settings = {'reload_templates':True, + 'mysetting':True} + config = self._makeOne(settings=settings) + settings = config.registry.getUtility(ISettings) + self.assertEqual(settings['reload_templates'], True) + self.assertEqual(settings['debug_authorization'], False) + self.assertEqual(settings['mysetting'], True) + + def test_ctor_noreg_debug_logger_None_default(self): + from pyramid.interfaces import IDebugLogger + config = self._makeOne() + logger = config.registry.getUtility(IDebugLogger) + self.assertEqual(logger.name, 'pyramid.debug') + + def test_ctor_noreg_debug_logger_non_None(self): + from pyramid.interfaces import IDebugLogger + logger = object() + config = self._makeOne(debug_logger=logger) + result = config.registry.getUtility(IDebugLogger) + self.assertEqual(logger, result) + + def test_ctor_authentication_policy(self): + from pyramid.interfaces import IAuthenticationPolicy + policy = object() + config = self._makeOne(authentication_policy=policy) + result = config.registry.getUtility(IAuthenticationPolicy) + self.assertEqual(policy, result) + + def test_ctor_authorization_policy_only(self): + from pyramid.exceptions import ConfigurationError + policy = object() + self.assertRaises(ConfigurationError, + self._makeOne, authorization_policy=policy) + + def test_ctor_no_root_factory(self): + from pyramid.interfaces import IRootFactory + config = self._makeOne() + self.failUnless(config.registry.getUtility(IRootFactory)) + + def test_ctor_alternate_renderers(self): + from pyramid.interfaces import IRendererFactory + renderer = object() + config = self._makeOne(renderers=[('yeah', renderer)]) + self.assertEqual(config.registry.getUtility(IRendererFactory, 'yeah'), + renderer) + + def test_ctor_default_permission(self): + from pyramid.interfaces import IDefaultPermission + config = self._makeOne(default_permission='view') + self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view') + + def test_ctor_session_factory(self): + from pyramid.interfaces import ISessionFactory + config = self._makeOne(session_factory='factory') + self.assertEqual(config.registry.getUtility(ISessionFactory), 'factory') + + def test_with_package_module(self): + from pyramid.tests import test_configuration + import pyramid.tests + config = self._makeOne() + newconfig = config.with_package(test_configuration) + self.assertEqual(newconfig.package, pyramid.tests) + + def test_with_package_package(self): + import pyramid.tests + config = self._makeOne() + newconfig = config.with_package(pyramid.tests) + self.assertEqual(newconfig.package, pyramid.tests) + + def test_maybe_dotted_string_success(self): + import pyramid.tests + config = self._makeOne() + result = config.maybe_dotted('pyramid.tests') + self.assertEqual(result, pyramid.tests) + + def test_maybe_dotted_string_fail(self): + config = self._makeOne() + self.assertRaises(ImportError, + config.maybe_dotted, 'cant.be.found') + + def test_maybe_dotted_notstring_success(self): + import pyramid.tests + config = self._makeOne() + result = config.maybe_dotted(pyramid.tests) + self.assertEqual(result, pyramid.tests) + + def test_absolute_resource_spec_already_absolute(self): + import pyramid.tests + config = self._makeOne(package=pyramid.tests) + result = config.absolute_resource_spec('already:absolute') + self.assertEqual(result, 'already:absolute') + + def test_absolute_resource_spec_notastring(self): + import pyramid.tests + config = self._makeOne(package=pyramid.tests) + result = config.absolute_resource_spec(None) + self.assertEqual(result, None) + + def test_absolute_resource_spec_relative(self): + import pyramid.tests + config = self._makeOne(package=pyramid.tests) + result = config.absolute_resource_spec('templates') + self.assertEqual(result, 'pyramid.tests:templates') + + def test_setup_registry_fixed(self): + class DummyRegistry(object): + def subscribers(self, events, name): + self.events = events + return events + def registerUtility(self, *arg, **kw): + pass + reg = DummyRegistry() + config = self._makeOne(reg) + config.add_view = lambda *arg, **kw: False + config.setup_registry() + self.assertEqual(reg.has_listeners, True) + self.assertEqual(reg.notify(1), None) + self.assertEqual(reg.events, (1,)) + + def test_setup_registry_registers_default_exceptionresponse_view(self): + from pyramid.interfaces import IExceptionResponse + from pyramid.view import default_exceptionresponse_view + class DummyRegistry(object): + def registerUtility(self, *arg, **kw): + pass + reg = DummyRegistry() + config = self._makeOne(reg) + views = [] + config.add_view = lambda *arg, **kw: views.append((arg, kw)) + config.setup_registry() + self.assertEqual(views[0], ((default_exceptionresponse_view,), + {'context':IExceptionResponse})) + + def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.exceptions import NotFound + from pyramid.registry import Registry + reg = Registry() + config = self._makeOne(reg, autocommit=True) + config.setup_registry() # registers IExceptionResponse default view + def myview(context, request): + return 'OK' + config.add_view(myview, context=NotFound) + request = self._makeRequest(config) + view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, 'OK') + + def test_setup_registry_custom_settings(self): + from pyramid.registry import Registry + from pyramid.interfaces import ISettings + settings = {'reload_templates':True, + 'mysetting':True} + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(settings=settings) + settings = reg.getUtility(ISettings) + self.assertEqual(settings['reload_templates'], True) + self.assertEqual(settings['debug_authorization'], False) + self.assertEqual(settings['mysetting'], True) + + def test_setup_registry_debug_logger_None_default(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDebugLogger + reg = Registry() + config = self._makeOne(reg) + config.setup_registry() + logger = reg.getUtility(IDebugLogger) + self.assertEqual(logger.name, 'pyramid.debug') + + def test_setup_registry_debug_logger_non_None(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDebugLogger + logger = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(debug_logger=logger) + result = reg.getUtility(IDebugLogger) + self.assertEqual(logger, result) + + def test_setup_registry_debug_logger_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDebugLogger + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(debug_logger='pyramid.tests') + result = reg.getUtility(IDebugLogger) + import pyramid.tests + self.assertEqual(result, pyramid.tests) + + def test_setup_registry_authentication_policy(self): + from pyramid.registry import Registry + from pyramid.interfaces import IAuthenticationPolicy + policy = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(authentication_policy=policy) + result = reg.getUtility(IAuthenticationPolicy) + self.assertEqual(policy, result) + + def test_setup_registry_authentication_policy_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IAuthenticationPolicy + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(authentication_policy='pyramid.tests') + result = reg.getUtility(IAuthenticationPolicy) + import pyramid.tests + self.assertEqual(result, pyramid.tests) + + def test_setup_registry_authorization_policy_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IAuthorizationPolicy + reg = Registry() + config = self._makeOne(reg) + dummy = object() + config.setup_registry(authentication_policy=dummy, + authorization_policy='pyramid.tests') + result = reg.getUtility(IAuthorizationPolicy) + import pyramid.tests + self.assertEqual(result, pyramid.tests) + + def test_setup_registry_authorization_policy_only(self): + from pyramid.registry import Registry + from pyramid.exceptions import ConfigurationError + policy = object() + reg = Registry() + config = self._makeOne(reg) + config = self.assertRaises(ConfigurationError, + config.setup_registry, + authorization_policy=policy) + + def test_setup_registry_default_root_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRootFactory + reg = Registry() + config = self._makeOne(reg) + config.setup_registry() + self.failUnless(reg.getUtility(IRootFactory)) + + def test_setup_registry_dottedname_root_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRootFactory + reg = Registry() + config = self._makeOne(reg) + import pyramid.tests + config.setup_registry(root_factory='pyramid.tests') + self.assertEqual(reg.getUtility(IRootFactory), pyramid.tests) + + def test_setup_registry_locale_negotiator_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import ILocaleNegotiator + reg = Registry() + config = self._makeOne(reg) + import pyramid.tests + config.setup_registry(locale_negotiator='pyramid.tests') + utility = reg.getUtility(ILocaleNegotiator) + self.assertEqual(utility, pyramid.tests) + + def test_setup_registry_locale_negotiator(self): + from pyramid.registry import Registry + from pyramid.interfaces import ILocaleNegotiator + reg = Registry() + config = self._makeOne(reg) + negotiator = object() + config.setup_registry(locale_negotiator=negotiator) + utility = reg.getUtility(ILocaleNegotiator) + self.assertEqual(utility, negotiator) + + def test_setup_registry_request_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRequestFactory + reg = Registry() + config = self._makeOne(reg) + factory = object() + config.setup_registry(request_factory=factory) + utility = reg.getUtility(IRequestFactory) + self.assertEqual(utility, factory) + + def test_setup_registry_request_factory_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRequestFactory + reg = Registry() + config = self._makeOne(reg) + import pyramid.tests + config.setup_registry(request_factory='pyramid.tests') + utility = reg.getUtility(IRequestFactory) + self.assertEqual(utility, pyramid.tests) + + def test_setup_registry_renderer_globals_factory(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRendererGlobalsFactory + reg = Registry() + config = self._makeOne(reg) + factory = object() + config.setup_registry(renderer_globals_factory=factory) + utility = reg.getUtility(IRendererGlobalsFactory) + self.assertEqual(utility, factory) + + def test_setup_registry_renderer_globals_factory_dottedname(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRendererGlobalsFactory + reg = Registry() + config = self._makeOne(reg) + import pyramid.tests + config.setup_registry(renderer_globals_factory='pyramid.tests') + utility = reg.getUtility(IRendererGlobalsFactory) + self.assertEqual(utility, pyramid.tests) + + def test_setup_registry_alternate_renderers(self): + from pyramid.registry import Registry + from pyramid.interfaces import IRendererFactory + renderer = object() + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(renderers=[('yeah', renderer)]) + self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'), + renderer) + + def test_setup_registry_default_permission(self): + from pyramid.registry import Registry + from pyramid.interfaces import IDefaultPermission + reg = Registry() + config = self._makeOne(reg) + config.setup_registry(default_permission='view') + self.assertEqual(reg.getUtility(IDefaultPermission), 'view') + + def test_get_settings_nosettings(self): + from pyramid.registry import Registry + reg = Registry() + config = self._makeOne(reg) + self.assertEqual(config.get_settings(), None) + + def test_get_settings_withsettings(self): + settings = {'a':1} + config = self._makeOne() + config.registry.settings = settings + self.assertEqual(config.get_settings(), settings) + + def test_add_settings_settings_already_registered(self): + from pyramid.registry import Registry + reg = Registry() + config = self._makeOne(reg) + config._set_settings({'a':1}) + config.add_settings({'b':2}) + settings = reg.settings + self.assertEqual(settings['a'], 1) + self.assertEqual(settings['b'], 2) + + def test_add_settings_settings_not_yet_registered(self): + from pyramid.registry import Registry + from pyramid.interfaces import ISettings + reg = Registry() + config = self._makeOne(reg) + config.add_settings({'a':1}) + settings = reg.getUtility(ISettings) + self.assertEqual(settings['a'], 1) + + def test_add_subscriber_defaults(self): + from zope.interface import implements + from zope.interface import Interface + class IEvent(Interface): + pass + class Event: + implements(IEvent) + L = [] + def subscriber(event): + L.append(event) + config = self._makeOne(autocommit=True) + config.add_subscriber(subscriber) + event = Event() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 2) + + def test_add_subscriber_iface_specified(self): + from zope.interface import implements + from zope.interface import Interface + class IEvent(Interface): + pass + class Event: + implements(IEvent) + L = [] + def subscriber(event): + L.append(event) + config = self._makeOne(autocommit=True) + config.add_subscriber(subscriber, IEvent) + event = Event() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_add_subscriber_dottednames(self): + import pyramid.tests + from pyramid.interfaces import INewRequest + config = self._makeOne(autocommit=True) + config.add_subscriber('pyramid.tests', + 'pyramid.interfaces.INewRequest') + handlers = list(config.registry.registeredHandlers()) + self.assertEqual(len(handlers), 1) + handler = handlers[0] + self.assertEqual(handler.handler, pyramid.tests) + self.assertEqual(handler.required, (INewRequest,)) + + def test_add_object_event_subscriber(self): + from zope.interface import implements + from zope.interface import Interface + class IEvent(Interface): + pass + class Event: + object = 'foo' + implements(IEvent) + event = Event() + L = [] + def subscriber(object, event): + L.append(event) + config = self._makeOne(autocommit=True) + config.add_subscriber(subscriber, (Interface, IEvent)) + config.registry.subscribers((event.object, event), None) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.subscribers((event.object, IDummy), None) + self.assertEqual(len(L), 1) + + def test_make_wsgi_app(self): + from pyramid.router import Router + from pyramid.interfaces import IApplicationCreated + manager = DummyThreadLocalManager() + config = self._makeOne() + subscriber = self._registerEventListener(config, IApplicationCreated) + config.manager = manager + app = config.make_wsgi_app() + self.assertEqual(app.__class__, Router) + self.assertEqual(manager.pushed['registry'], config.registry) + self.assertEqual(manager.pushed['request'], None) + self.failUnless(manager.popped) + self.assertEqual(len(subscriber), 1) + self.failUnless(IApplicationCreated.providedBy(subscriber[0])) + + def test_load_zcml_default(self): + import pyramid.tests.fixtureapp + config = self._makeOne(package=pyramid.tests.fixtureapp, + autocommit=True) + registry = config.load_zcml() + from pyramid.tests.fixtureapp.models import IFixture + self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_routesapp(self): + from pyramid.interfaces import IRoutesMapper + config = self._makeOne(autocommit=True) + config.load_zcml('pyramid.tests.routesapp:configure.zcml') + self.failUnless(config.registry.getUtility(IRoutesMapper)) + + def test_load_zcml_fixtureapp(self): + from pyramid.tests.fixtureapp.models import IFixture + config = self._makeOne(autocommit=True) + config.load_zcml('pyramid.tests.fixtureapp:configure.zcml') + self.failUnless(config.registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_as_relative_filename(self): + import pyramid.tests.fixtureapp + config = self._makeOne(package=pyramid.tests.fixtureapp, + autocommit=True) + registry = config.load_zcml('configure.zcml') + from pyramid.tests.fixtureapp.models import IFixture + self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_as_absolute_filename(self): + import os + import pyramid.tests.fixtureapp + config = self._makeOne(package=pyramid.tests.fixtureapp, + autocommit=True) + dn = os.path.dirname(pyramid.tests.fixtureapp.__file__) + c_z = os.path.join(dn, 'configure.zcml') + registry = config.load_zcml(c_z) + from pyramid.tests.fixtureapp.models import IFixture + self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml + + def test_load_zcml_lock_and_unlock(self): + config = self._makeOne(autocommit=True) + dummylock = DummyLock() + config.load_zcml( + 'pyramid.tests.fixtureapp:configure.zcml', + lock=dummylock) + self.assertEqual(dummylock.acquired, True) + self.assertEqual(dummylock.released, True) + + def test_include_with_dotted_name(self): + from pyramid import tests + config = self._makeOne() + context_before = config._make_context() + config._ctx = context_before + config.include('pyramid.tests.test_config.dummy_include') + context_after = config._ctx + actions = context_after.actions + self.assertEqual(len(actions), 1) + self.assertEqual( + context_after.actions[0][:3], + ('discrim', None, tests), + ) + self.assertEqual(context_after.basepath, None) + self.assertEqual(context_after.includepath, ()) + self.failUnless(context_after is context_before) + + def test_include_with_python_callable(self): + from pyramid import tests + config = self._makeOne() + context_before = config._make_context() + config._ctx = context_before + config.include(dummy_include) + context_after = config._ctx + actions = context_after.actions + self.assertEqual(len(actions), 1) + self.assertEqual( + actions[0][:3], + ('discrim', None, tests), + ) + self.assertEqual(context_after.basepath, None) + self.assertEqual(context_after.includepath, ()) + self.failUnless(context_after is context_before) + + def test_with_context(self): + config = self._makeOne() + ctx = config._make_context() + newconfig = config.with_context(ctx) + self.assertEqual(newconfig._ctx, ctx) + + def test_add_view_view_callable_None_no_renderer(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne(autocommit=True) + self.assertRaises(ConfigurationError, config.add_view) + + def test_add_view_with_request_type_and_route_name(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + self.assertRaises(ConfigurationError, config.add_view, view, '', None, + None, True, True) + + def test_add_view_with_request_type(self): + from zope.interface import directlyProvides + from pyramid.interfaces import IRequest + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, + request_type='pyramid.interfaces.IRequest') + wrapper = self._getViewCallable(config) + request = DummyRequest() + self._assertNotFound(wrapper, None, request) + directlyProvides(request, IRequest) + result = wrapper(None, request) + self.assertEqual(result, 'OK') + + def test_add_view_view_callable_None_with_renderer(self): + config = self._makeOne(autocommit=True) + self._registerRenderer(config, name='dummy') + config.add_view(renderer='dummy') + view = self._getViewCallable(config) + self.failUnless('Hello!' in view(None, None).body) + + def test_add_view_wrapped_view_is_decorated(self): + def view(request): # request-only wrapper + """ """ + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + self.assertEqual(wrapper.__module__, view.__module__) + self.assertEqual(wrapper.__name__, view.__name__) + self.assertEqual(wrapper.__doc__, view.__doc__) + + def test_add_view_view_callable_dottedname(self): + config = self._makeOne(autocommit=True) + config.add_view(view='pyramid.tests.test_config.dummy_view') + wrapper = self._getViewCallable(config) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_with_function_callable(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_with_function_callable_requestonly(self): + def view(request): + return 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_instance(self): + class AView: + def __call__(self, context, request): + """ """ + return 'OK' + view = AView() + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_instance_requestonly(self): + class AView: + def __call__(self, request): + """ """ + return 'OK' + view = AView() + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_oldstyle_class(self): + class view: + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, request): + self.request = request + + def __call__(self): + return 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = self._getViewCallable(config) + result = wrapper(None, None) + self.assertEqual(result, 'OK') + + def test_add_view_context_as_class(self): + from zope.interface import implementedBy + view = lambda *arg: 'OK' + class Foo: + pass + config = self._makeOne(autocommit=True) + config.add_view(context=Foo, view=view) + foo = implementedBy(Foo) + wrapper = self._getViewCallable(config, foo) + self.assertEqual(wrapper, view) + + def test_add_view_context_as_iface(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(context=IDummy, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_context_as_dottedname(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(context='pyramid.tests.test_config.IDummy', + view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_for__as_dottedname(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(for_='pyramid.tests.test_config.IDummy', + view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_for_as_class(self): + # ``for_`` is older spelling for ``context`` + from zope.interface import implementedBy + view = lambda *arg: 'OK' + class Foo: + pass + config = self._makeOne(autocommit=True) + config.add_view(for_=Foo, view=view) + foo = implementedBy(Foo) + wrapper = self._getViewCallable(config, foo) + self.assertEqual(wrapper, view) + + def test_add_view_for_as_iface(self): + # ``for_`` is older spelling for ``context`` + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(for_=IDummy, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_context_trumps_for(self): + # ``for_`` is older spelling for ``context`` + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + class Foo: + pass + config.add_view(context=IDummy, for_=Foo, view=view) + wrapper = self._getViewCallable(config, IDummy) + self.assertEqual(wrapper, view) + + def test_add_view_register_secured_view(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import ISecuredView + from pyramid.interfaces import IViewClassifier + view = lambda *arg: 'OK' + view.__call_permissive__ = view + config = self._makeOne(autocommit=True) + config.add_view(view=view) + wrapper = config.registry.adapters.lookup( + (IViewClassifier, IRequest, Interface), + ISecuredView, name='', default=None) + self.assertEqual(wrapper, view) + + def test_add_view_exception_register_secured_view(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + view = lambda *arg: 'OK' + view.__call_permissive__ = view + config = self._makeOne(autocommit=True) + config.add_view(view=view, context=RuntimeError) + wrapper = config.registry.adapters.lookup( + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='', default=None) + self.assertEqual(wrapper, view) + + def test_add_view_same_phash_overrides_existing_single_view(self): + from pyramid.compat import md5 + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + phash = md5() + phash.update('xhr:True') + view = lambda *arg: 'NOT OK' + view.__phash__ = phash.hexdigest() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, xhr=True) + wrapper = self._getViewCallable(config) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_same_phash_overrides_existing_single_view(self): + from pyramid.compat import md5 + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IMultiView + phash = md5() + phash.update('xhr:True') + view = lambda *arg: 'NOT OK' + view.__phash__ = phash.hexdigest() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, xhr=True, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_default_phash_overrides_no_phash(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview) + wrapper = self._getViewCallable(config) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_default_phash_overrides_no_phash(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_default_phash_overrides_default_phash(self): + from pyramid.config import DEFAULT_PHASH + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + view.__phash__ = DEFAULT_PHASH + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview) + wrapper = self._getViewCallable(config) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_default_phash_overrides_default_phash(self): + from pyramid.config import DEFAULT_PHASH + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'NOT OK' + view.__phash__ = DEFAULT_PHASH + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + def newview(context, request): + return 'OK' + config.add_view(view=newview, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failIf(IMultiView.providedBy(wrapper)) + request = DummyRequest() + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_multiview_replaces_existing_view(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + config.add_view(view=view) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_exc_multiview_replaces_existing_view(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IExceptionViewClassifier + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IMultiView + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.add_view(view=view, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_multiview_replaces_existing_securedview(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import ISecuredView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), + ISecuredView, name='') + config.add_view(view=view) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_exc_multiview_replaces_existing_securedview(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import ISecuredView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + view = lambda *arg: 'OK' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + ISecuredView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + ISecuredView, name='') + config.add_view(view=view, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + + def test_add_view_with_accept_multiview_replaces_existing_view(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + def view(context, request): + return 'OK' + def view2(context, request): + return 'OK2' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + config.add_view(view=view2, accept='text/html') + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK') + request = DummyRequest() + request.accept = DummyAccept('text/html', 'text/html') + self.assertEqual(wrapper(None, request), 'OK2') + + def test_add_view_exc_with_accept_multiview_replaces_existing_view(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + def view(context, request): + return 'OK' + def view2(context, request): + return 'OK2' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.add_view(view=view2, accept='text/html', context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK') + request = DummyRequest() + request.accept = DummyAccept('text/html', 'text/html') + self.assertEqual(wrapper(None, request), 'OK2') + + def test_add_view_multiview_replaces_existing_view_with___accept__(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + def view(context, request): + return 'OK' + def view2(context, request): + return 'OK2' + view.__accept__ = 'text/html' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), IView, name='') + config.add_view(view=view2) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK2') + request = DummyRequest() + request.accept = DummyAccept('text/html') + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_exc_mulview_replaces_existing_view_with___accept__(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + def view(context, request): + return 'OK' + def view2(context, request): + return 'OK2' + view.__accept__ = 'text/html' + view.__phash__ = 'abc' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IView, name='') + config.add_view(view=view2, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual(len(wrapper.views), 1) + self.assertEqual(len(wrapper.media_views), 1) + self.assertEqual(wrapper(None, None), 'OK2') + request = DummyRequest() + request.accept = DummyAccept('text/html') + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_multiview_replaces_multiview(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + view = DummyMultiView() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Interface), + IMultiView, name='') + view2 = lambda *arg: 'OK2' + config.add_view(view=view2) + wrapper = self._getViewCallable(config) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) + self.assertEqual(wrapper(None, None), 'OK1') + + def test_add_view_exc_multiview_replaces_multiview(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + view = DummyMultiView() + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, + (IViewClassifier, IRequest, implementedBy(RuntimeError)), + IMultiView, name='') + config.registry.registerAdapter( + view, + (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), + IMultiView, name='') + view2 = lambda *arg: 'OK2' + config.add_view(view=view2, context=RuntimeError) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), exception_view=True) + self.failUnless(IMultiView.providedBy(wrapper)) + self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) + self.assertEqual(wrapper(None, None), 'OK1') + + def test_add_view_multiview_context_superclass_then_subclass(self): + from zope.interface import Interface + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + class ISuper(Interface): + pass + class ISub(ISuper): + pass + view = lambda *arg: 'OK' + view2 = lambda *arg: 'OK2' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, ISuper), IView, name='') + config.add_view(view=view2, for_=ISub) + wrapper = self._getViewCallable(config, ISuper, IRequest) + self.failIf(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK') + wrapper = self._getViewCallable(config, ISub, IRequest) + self.failIf(IMultiView.providedBy(wrapper)) + self.assertEqual(wrapper(None, None), 'OK2') + + def test_add_view_multiview_exception_superclass_then_subclass(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.interfaces import IView + from pyramid.interfaces import IMultiView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IExceptionViewClassifier + class Super(Exception): + pass + class Sub(Super): + pass + view = lambda *arg: 'OK' + view2 = lambda *arg: 'OK2' + config = self._makeOne(autocommit=True) + config.registry.registerAdapter( + view, (IViewClassifier, IRequest, Super), IView, name='') + config.registry.registerAdapter( + view, (IExceptionViewClassifier, IRequest, Super), IView, name='') + config.add_view(view=view2, for_=Sub) + wrapper = self._getViewCallable( + config, implementedBy(Super), IRequest) + wrapper_exc_view = self._getViewCallable( + config, implementedBy(Super), IRequest, exception_view=True) + self.assertEqual(wrapper_exc_view, wrapper) + self.failIf(IMultiView.providedBy(wrapper_exc_view)) + self.assertEqual(wrapper_exc_view(None, None), 'OK') + wrapper = self._getViewCallable( + config, implementedBy(Sub), IRequest) + wrapper_exc_view = self._getViewCallable( + config, implementedBy(Sub), IRequest, exception_view=True) + self.assertEqual(wrapper_exc_view, wrapper) + self.failIf(IMultiView.providedBy(wrapper_exc_view)) + self.assertEqual(wrapper_exc_view(None, None), 'OK2') + + def test_add_view_multiview_call_ordering(self): + from zope.interface import directlyProvides + def view1(context, request): return 'view1' + def view2(context, request): return 'view2' + def view3(context, request): return 'view3' + def view4(context, request): return 'view4' + def view5(context, request): return 'view5' + def view6(context, request): return 'view6' + def view7(context, request): return 'view7' + def view8(context, request): return 'view8' + config = self._makeOne(autocommit=True) + config.add_view(view=view1) + config.add_view(view=view2, request_method='POST') + config.add_view(view=view3,request_param='param') + config.add_view(view=view4, containment=IDummy) + config.add_view(view=view5, request_method='POST',request_param='param') + config.add_view(view=view6, request_method='POST', containment=IDummy) + config.add_view(view=view7, request_param='param', containment=IDummy) + config.add_view(view=view8, request_method='POST',request_param='param', + containment=IDummy) + + + wrapper = self._getViewCallable(config) + + ctx = DummyContext() + request = self._makeRequest(config) + request.method = 'GET' + request.params = {} + self.assertEqual(wrapper(ctx, request), 'view1') + + ctx = DummyContext() + request = self._makeRequest(config) + request.params = {} + request.method = 'POST' + self.assertEqual(wrapper(ctx, request), 'view2') + + ctx = DummyContext() + request = self._makeRequest(config) + request.params = {'param':'1'} + request.method = 'GET' + self.assertEqual(wrapper(ctx, request), 'view3') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'GET' + request.params = {} + self.assertEqual(wrapper(ctx, request), 'view4') + + ctx = DummyContext() + request = self._makeRequest(config) + request.method = 'POST' + request.params = {'param':'1'} + self.assertEqual(wrapper(ctx, request), 'view5') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.params = {} + request.method = 'POST' + self.assertEqual(wrapper(ctx, request), 'view6') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'GET' + request.params = {'param':'1'} + self.assertEqual(wrapper(ctx, request), 'view7') + + ctx = DummyContext() + directlyProvides(ctx, IDummy) + request = self._makeRequest(config) + request.method = 'POST' + request.params = {'param':'1'} + self.assertEqual(wrapper(ctx, request), 'view8') + + def test_add_view_with_template_renderer(self): + import pyramid.tests + from pyramid.interfaces import ISettings + class view(object): + def __init__(self, context, request): + self.request = request + self.context = context + + def __call__(self): + return {'a':'1'} + config = self._makeOne(autocommit=True) + renderer = self._registerRenderer(config) + fixture = 'pyramid.tests:fixtures/minimal.txt' + config.add_view(view=view, renderer=fixture) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result.body, 'Hello!') + settings = config.registry.queryUtility(ISettings) + result = renderer.info + self.assertEqual(result.registry, config.registry) + self.assertEqual(result.type, '.txt') + self.assertEqual(result.package, pyramid.tests) + self.assertEqual(result.name, fixture) + self.assertEqual(result.settings, settings) + + def test_add_view_with_template_renderer_no_callable(self): + import pyramid.tests + from pyramid.interfaces import ISettings + config = self._makeOne(autocommit=True) + renderer = self._registerRenderer(config) + fixture = 'pyramid.tests:fixtures/minimal.txt' + config.add_view(view=None, renderer=fixture) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + result = wrapper(None, request) + self.assertEqual(result.body, 'Hello!') + settings = config.registry.queryUtility(ISettings) + result = renderer.info + self.assertEqual(result.registry, config.registry) + self.assertEqual(result.type, '.txt') + self.assertEqual(result.package, pyramid.tests) + self.assertEqual(result.name, fixture) + self.assertEqual(result.settings, settings) + + def test_add_view_with_request_type_as_iface(self): + from zope.interface import directlyProvides + def view(context, request): + return 'OK' + config = self._makeOne(autocommit=True) + config.add_view(request_type=IDummy, view=view) + wrapper = self._getViewCallable(config, None) + request = self._makeRequest(config) + directlyProvides(request, IDummy) + result = wrapper(None, request) + self.assertEqual(result, 'OK') + + def test_add_view_with_request_type_as_noniface(self): + from pyramid.exceptions import ConfigurationError + view = lambda *arg: 'OK' + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_view, view, '', None, None, object) + + def test_add_view_with_route_name(self): + from zope.component import ComponentLookupError + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, route_name='foo') + self.assertEqual(len(config.registry.deferred_route_views), 1) + infos = config.registry.deferred_route_views['foo'] + self.assertEqual(len(infos), 1) + info = infos[0] + self.assertEqual(info['route_name'], 'foo') + self.assertEqual(info['view'], view) + self.assertRaises(ComponentLookupError, + self._getRouteRequestIface, config, 'foo') + wrapper = self._getViewCallable(config, None) + self.assertEqual(wrapper, None) + config.add_route('foo', '/a/b') + request_iface = self._getRouteRequestIface(config, 'foo') + self.failIfEqual(request_iface, None) + wrapper = self._getViewCallable(config, request_iface=request_iface) + self.failIfEqual(wrapper, None) + self.assertEqual(wrapper(None, None), 'OK') + + def test_deferred_route_views_retains_custom_predicates(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, route_name='foo', custom_predicates=('123',)) + self.assertEqual(len(config.registry.deferred_route_views), 1) + infos = config.registry.deferred_route_views['foo'] + self.assertEqual(len(infos), 1) + info = infos[0] + self.assertEqual(info['route_name'], 'foo') + self.assertEqual(info['custom_predicates'], ('123',)) + + def test_add_view_with_route_name_exception(self): + from zope.interface import implementedBy + from zope.component import ComponentLookupError + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, route_name='foo', context=RuntimeError) + self.assertEqual(len(config.registry.deferred_route_views), 1) + infos = config.registry.deferred_route_views['foo'] + self.assertEqual(len(infos), 1) + info = infos[0] + self.assertEqual(info['route_name'], 'foo') + self.assertEqual(info['view'], view) + self.assertRaises(ComponentLookupError, + self._getRouteRequestIface, config, 'foo') + wrapper_exc_view = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + exception_view=True) + self.assertEqual(wrapper_exc_view, None) + config.add_route('foo', '/a/b') + request_iface = self._getRouteRequestIface(config, 'foo') + self.failIfEqual(request_iface, None) + wrapper_exc_view = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + request_iface=request_iface, exception_view=True) + self.failIfEqual(wrapper_exc_view, None) + wrapper = self._getViewCallable( + config, ctx_iface=implementedBy(RuntimeError), + request_iface=request_iface) + self.assertEqual(wrapper_exc_view, wrapper) + self.assertEqual(wrapper_exc_view(None, None), 'OK') + + def test_add_view_with_request_method_true(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_method='POST') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'POST' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_method_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_method='POST') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'GET' + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_request_param_noval_true(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {'abc':''} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_param_noval_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_request_param_val_true(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc=123') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {'abc':'123'} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_request_param_val_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_param='abc=123') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.params = {'abc':''} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_xhr_true(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, xhr=True) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_xhr_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, xhr=True) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.is_xhr = False + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_header_badregex(self): + from pyramid.exceptions import ConfigurationError + view = lambda *arg: 'OK' + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_view, view=view, header='Host:a\\') + + def test_add_view_with_header_noval_match(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header='Host') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'Host':'whatever'} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_header_noval_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header='Host') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'NotHost':'whatever'} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_header_val_match(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header=r'Host:\d') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'Host':'1'} + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_header_val_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header=r'Host:\d') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'Host':'abc'} + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_header_val_missing(self): + from pyramid.exceptions import NotFound + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, header=r'Host:\d') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.headers = {'NoHost':'1'} + self.assertRaises(NotFound, wrapper, None, request) + + def test_add_view_with_accept_match(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, accept='text/xml') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.accept = ['text/xml'] + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_accept_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, accept='text/xml') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.accept = ['text/html'] + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_containment_true(self): + from zope.interface import directlyProvides + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, containment=IDummy) + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + self.assertEqual(wrapper(context, None), 'OK') + + def test_add_view_with_containment_false(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, containment=IDummy) + wrapper = self._getViewCallable(config) + context = DummyContext() + self._assertNotFound(wrapper, context, None) + + def test_add_view_with_containment_dottedname(self): + from zope.interface import directlyProvides + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view( + view=view, + containment='pyramid.tests.test_config.IDummy') + wrapper = self._getViewCallable(config) + context = DummyContext() + directlyProvides(context, IDummy) + self.assertEqual(wrapper(context, None), 'OK') + + def test_add_view_with_path_info_badregex(self): + from pyramid.exceptions import ConfigurationError + view = lambda *arg: 'OK' + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_view, view=view, path_info='\\') + + def test_add_view_with_path_info_match(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, path_info='/foo') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.path_info = '/foo' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_path_info_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, path_info='/foo') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.path_info = '/' + self._assertNotFound(wrapper, None, request) + + def test_add_view_with_custom_predicates_match(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + def pred1(context, request): + return True + def pred2(context, request): + return True + predicates = (pred1, pred2) + config.add_view(view=view, custom_predicates=predicates) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_with_custom_predicates_nomatch(self): + view = lambda *arg: 'OK' + config = self._makeOne(autocommit=True) + def pred1(context, request): + return True + def pred2(context, request): + return False + predicates = (pred1, pred2) + config.add_view(view=view, custom_predicates=predicates) + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + self._assertNotFound(wrapper, None, request) + + def test_add_view_custom_predicate_bests_standard_predicate(self): + view = lambda *arg: 'OK' + view2 = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + def pred1(context, request): + return True + config.add_view(view=view, custom_predicates=(pred1,)) + config.add_view(view=view2, request_method='GET') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'GET' + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_custom_more_preds_first_bests_fewer_preds_last(self): + view = lambda *arg: 'OK' + view2 = lambda *arg: 'NOT OK' + config = self._makeOne(autocommit=True) + config.add_view(view=view, request_method='GET', xhr=True) + config.add_view(view=view2, request_method='GET') + wrapper = self._getViewCallable(config) + request = self._makeRequest(config) + request.method = 'GET' + request.is_xhr = True + self.assertEqual(wrapper(None, request), 'OK') + + def test_add_view_same_predicates(self): + from zope.configuration.config import ConfigurationConflictError + view2 = lambda *arg: 'second' + view1 = lambda *arg: 'first' + config = self._makeOne() + config.add_view(view=view1) + config.add_view(view=view2) + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_add_view_with_permission(self): + view1 = lambda *arg: 'OK' + outerself = self + class DummyPolicy(object): + def effective_principals(self, r): + outerself.assertEqual(r, request) + return ['abc'] + def permits(self, context, principals, permission): + outerself.assertEqual(context, None) + outerself.assertEqual(principals, ['abc']) + outerself.assertEqual(permission, 'view') + return True + policy = DummyPolicy() + config = self._makeOne(authorization_policy=policy, + authentication_policy=policy, + autocommit=True) + config.add_view(view=view1, permission='view') + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'OK') + + def test_add_view_with_default_permission_no_explicit_permission(self): + view1 = lambda *arg: 'OK' + outerself = self + class DummyPolicy(object): + def effective_principals(self, r): + outerself.assertEqual(r, request) + return ['abc'] + def permits(self, context, principals, permission): + outerself.assertEqual(context, None) + outerself.assertEqual(principals, ['abc']) + outerself.assertEqual(permission, 'view') + return True + policy = DummyPolicy() + config = self._makeOne(authorization_policy=policy, + authentication_policy=policy, + default_permission='view', + autocommit=True) + config.add_view(view=view1) + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'OK') + + def test_add_view_with_no_default_permission_no_explicit_permission(self): + view1 = lambda *arg: 'OK' + class DummyPolicy(object): pass # wont be called + policy = DummyPolicy() + config = self._makeOne(authorization_policy=policy, + authentication_policy=policy, + autocommit=True) + config.add_view(view=view1) + view = self._getViewCallable(config) + request = self._makeRequest(config) + self.assertEqual(view(None, request), 'OK') + + def test_add_handler_action_in_route_pattern(self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_handler('name', '/:action', DummyHandler) + self._assertRoute(config, 'name', '/:action', 0) + self.assertEqual(len(views), 2) + + view = views[0] + preds = view['custom_predicates'] + self.assertEqual(len(preds), 1) + pred = preds[0] + request = DummyRequest() + self.assertEqual(pred(None, request), False) + request.matchdict = {'action':'action1'} + self.assertEqual(pred(None, request), True) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['attr'], 'action1') + self.assertEqual(view['view'], DummyHandler) + + view = views[1] + preds = view['custom_predicates'] + self.assertEqual(len(preds), 1) + pred = preds[0] + request = DummyRequest() + self.assertEqual(pred(None, request), False) + request.matchdict = {'action':'action2'} + self.assertEqual(pred(None, request), True) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['attr'], 'action2') + self.assertEqual(view['view'], DummyHandler) + + def test_add_handler_with_view_overridden_autoexpose_None(self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) # pragma: no cover + config.add_view = dummy_add_view + class MyView(DummyHandler): + __autoexpose__ = None + config.add_handler('name', '/:action', MyView) + self._assertRoute(config, 'name', '/:action', 0) + self.assertEqual(len(views), 0) + + def test_add_handler_with_view_overridden_autoexpose_broken_regex1(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + def dummy_add_view(**kw): + """ """ + config.add_view = dummy_add_view + class MyView(DummyHandler): + __autoexpose__ = 1 + self.assertRaises(ConfigurationError, config.add_handler, + 'name', '/{action}', MyView) + + def test_add_handler_with_view_overridden_autoexpose_broken_regex2(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + def dummy_add_view(**kw): + """ """ + config.add_view = dummy_add_view + class MyView(DummyHandler): + __autoexpose__ = 'a\\' + self.assertRaises(ConfigurationError, config.add_handler, + 'name', '/{action}', MyView) + + def test_add_handler_with_view_method_has_expose_config(self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + class MyView(object): + def action(self): # pragma: no cover + return 'response' + action.__exposed__ = [{'custom_predicates':(1,)}] + config.add_handler('name', '/:action', MyView) + self._assertRoute(config, 'name', '/:action', 0) + self.assertEqual(len(views), 1) + view = views[0] + preds = view['custom_predicates'] + self.assertEqual(len(preds), 2) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['attr'], 'action') + self.assertEqual(view['view'], MyView) + + def test_add_handler_with_view_method_has_expose_config_with_action(self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + class MyView(object): + def action(self): # pragma: no cover + return 'response' + action.__exposed__ = [{'name':'action3000'}] + config.add_handler('name', '/:action', MyView) + self._assertRoute(config, 'name', '/:action', 0) + self.assertEqual(len(views), 1) + view = views[0] + preds = view['custom_predicates'] + self.assertEqual(len(preds), 1) + pred = preds[0] + request = DummyRequest() + self.assertEqual(pred(None, request), False) + request.matchdict = {'action':'action3000'} + self.assertEqual(pred(None, request), True) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['attr'], 'action') + self.assertEqual(view['view'], MyView) + + def test_add_handler_with_view_method_has_expose_config_with_action_regex( + self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + class MyView(object): + def action(self): # pragma: no cover + return 'response' + action.__exposed__ = [{'name':'^action3000$'}] + config.add_handler('name', '/:action', MyView) + self._assertRoute(config, 'name', '/:action', 0) + self.assertEqual(len(views), 1) + view = views[0] + preds = view['custom_predicates'] + self.assertEqual(len(preds), 1) + pred = preds[0] + request = DummyRequest() + self.assertEqual(pred(None, request), False) + request.matchdict = {'action':'action3000'} + self.assertEqual(pred(None, request), True) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['attr'], 'action') + self.assertEqual(view['view'], MyView) + + def test_add_handler_doesnt_mutate_expose_dict(self): + config = self._makeOne(autocommit=True) + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + exposed = [{'name':'^action3000$'}] + class MyView(object): + def action(self): # pragma: no cover + return 'response' + action.__exposed__ = exposed + config.add_handler('name', '/{action}', MyView) + self.assertEqual(exposed[0], {'name':'^action3000$'}) # not mutated + + def test_add_handler_with_action_and_action_in_path(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_handler, + 'name', '/{action}', DummyHandler, action='abc') + + def test_add_handler_with_explicit_action(self): + config = self._makeOne(autocommit=True) + class DummyHandler(object): + def index(self): pass + index.__exposed__ = [{'a':'1'}] + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_handler('name', '/abc', DummyHandler, action='index') + self.assertEqual(len(views), 1) + view = views[0] + self.assertEqual(view['a'], '1') + self.assertEqual(view['attr'], 'index') + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['view'], DummyHandler) + + def test_add_handler_with_implicit_action(self): + config = self._makeOne(autocommit=True) + class DummyHandler(object): + def __call__(self): pass + __call__.__exposed__ = [{'a':'1'}] + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_handler('name', '/abc', DummyHandler) + self.assertEqual(len(views), 1) + view = views[0] + self.assertEqual(view['a'], '1') + self.assertEqual(view['attr'], None) + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['view'], DummyHandler) + + def test_add_handler_with_multiple_action(self): + config = self._makeOne(autocommit=True) + class DummyHandler(object): + def index(self): pass + def create(self): pass + create.__exposed__ = [{'name': 'index'}] + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_handler('name', '/abc', DummyHandler, action='index') + self.assertEqual(len(views), 2) + view = views[0] + self.assertEqual(view['attr'], 'create') + self.assertEqual(view['route_name'], 'name') + self.assertEqual(view['view'], DummyHandler) + view = views[1] + self.assertEqual(view['attr'], 'index') + + def test_add_handler_string(self): + import pyramid + views = [] + config = self._makeOne(autocommit=True) + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_handler('name', '/abc', 'pyramid') + self.assertEqual(len(views), 1) + view = views[0] + self.assertEqual(view['view'], pyramid) + + def test_add_handler_pattern_None_no_previous_route(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_handler, + 'name', None, 'pyramid') + + def test_add_handler_pattern_None_with_previous_route(self): + import pyramid + config = self._makeOne(autocommit=True) + config.add_route('name', ':def') + views = [] + def dummy_add_view(**kw): + views.append(kw) + config.add_view = dummy_add_view + config.add_route = None # shouldn't be called + config.add_handler('name', None, 'pyramid') + self.assertEqual(len(views), 1) + view = views[0] + self.assertEqual(view['view'], pyramid) + + + 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 test_get_routes_mapper_not_yet_registered(self): + config = self._makeOne() + mapper = config.get_routes_mapper() + self.assertEqual(mapper.routelist, []) + + def test_get_routes_mapper_already_registered(self): + from pyramid.interfaces import IRoutesMapper + config = self._makeOne() + mapper = object() + config.registry.registerUtility(mapper, IRoutesMapper) + result = config.get_routes_mapper() + self.assertEqual(result, mapper) + + def test_add_route_defaults(self): + config = self._makeOne(autocommit=True) + route = config.add_route('name', 'path') + self._assertRoute(config, 'name', 'path') + self.assertEqual(route.name, 'name') + + def test_add_route_with_factory(self): + config = self._makeOne(autocommit=True) + factory = object() + route = config.add_route('name', 'path', factory=factory) + self.assertEqual(route.factory, factory) + + def test_add_route_with_factory_dottedname(self): + config = self._makeOne(autocommit=True) + route = config.add_route( + 'name', 'path', + factory='pyramid.tests.test_config.dummyfactory') + self.assertEqual(route.factory, dummyfactory) + + def test_add_route_with_xhr(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', xhr=True) + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.is_xhr = True + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.is_xhr = False + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_request_method(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', request_method='GET') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.method = 'GET' + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.method = 'POST' + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_path_info(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', path_info='/foo') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.path_info = '/foo' + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.path_info = '/' + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_request_param(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', request_param='abc') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.params = {'abc':'123'} + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.params = {} + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_custom_predicates(self): + config = self._makeOne(autocommit=True) + def pred1(context, request): pass + def pred2(context, request): pass + config.add_route('name', 'path', custom_predicates=(pred1, pred2)) + route = self._assertRoute(config, 'name', 'path', 2) + self.assertEqual(route.predicates, [pred1, pred2]) + + def test_add_route_with_header(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', header='Host') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.headers = {'Host':'example.com'} + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + request.headers = {} + self.assertEqual(predicate(None, request), False) + + def test_add_route_with_accept(self): + config = self._makeOne(autocommit=True) + config.add_route('name', 'path', accept='text/xml') + route = self._assertRoute(config, 'name', 'path', 1) + predicate = route.predicates[0] + request = self._makeRequest(config) + request.accept = ['text/xml'] + self.assertEqual(predicate(None, request), True) + request = self._makeRequest(config) + 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') + self.assertEqual(wrapper(None, None), '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.failUnless(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.failUnless(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') + self._assertRoute(config, 'name', 'path') + self.assertEqual(route.name, 'name') + + def test_add_route_no_path_no_pattern(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.add_route, 'name') + + def test_add_route_with_pregenerator(self): + config = self._makeOne(autocommit=True) + route = config.add_route('name', 'pattern', pregenerator='123') + self.assertEqual(route.pregenerator, '123') + + def test__override_not_yet_registered(self): + from pyramid.interfaces import IPackageOverrides + package = DummyPackage('package') + opackage = DummyPackage('opackage') + config = self._makeOne() + config._override(package, 'path', opackage, 'oprefix', + PackageOverrides=DummyOverrides) + overrides = config.registry.queryUtility(IPackageOverrides, + name='package') + self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) + self.assertEqual(overrides.package, package) + + def test__override_already_registered(self): + from pyramid.interfaces import IPackageOverrides + package = DummyPackage('package') + opackage = DummyPackage('opackage') + overrides = DummyOverrides(package) + config = self._makeOne() + config.registry.registerUtility(overrides, IPackageOverrides, + name='package') + config._override(package, 'path', opackage, 'oprefix', + PackageOverrides=DummyOverrides) + self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) + self.assertEqual(overrides.package, package) + + def test_add_static_here_no_utility_registered(self): + from pyramid.static import PackageURLParser + from zope.interface import implementedBy + from pyramid.static import StaticURLInfo + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + config = self._makeOne(autocommit=True) + config.add_static_view('static', 'fixtures/static') + request_type = self._getRouteRequestIface(config, 'static/') + route = self._assertRoute(config, 'static/', 'static/*subpath') + self.assertEqual(route.factory.__class__, type(lambda x: x)) + iface = implementedBy(StaticURLInfo) + wrapped = config.registry.adapters.lookup( + (IViewClassifier, request_type, iface), IView, name='') + request = self._makeRequest(config) + self.assertEqual(wrapped(None, request).__class__, PackageURLParser) + + def test_add_static_view_package_relative(self): + from pyramid.interfaces import IStaticURLInfo + info = DummyStaticURLInfo() + config = self._makeOne(autocommit=True) + config.registry.registerUtility(info, IStaticURLInfo) + config.add_static_view('static', 'pyramid.tests:fixtures/static') + self.assertEqual(info.added, + [('static', 'pyramid.tests:fixtures/static', {})]) + + def test_add_static_view_package_here_relative(self): + from pyramid.interfaces import IStaticURLInfo + info = DummyStaticURLInfo() + config = self._makeOne(autocommit=True) + config.registry.registerUtility(info, IStaticURLInfo) + config.add_static_view('static', 'fixtures/static') + self.assertEqual(info.added, + [('static', 'pyramid.tests:fixtures/static', {})]) + + def test_add_static_view_absolute(self): + import os + from pyramid.interfaces import IStaticURLInfo + info = DummyStaticURLInfo() + config = self._makeOne(autocommit=True) + config.registry.registerUtility(info, IStaticURLInfo) + here = os.path.dirname(__file__) + static_path = os.path.join(here, 'fixtures', 'static') + config.add_static_view('static', static_path) + self.assertEqual(info.added, + [('static', static_path, {})]) + + def test_set_notfound_view(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.exceptions import NotFound + config = self._makeOne(autocommit=True) + view = lambda *arg: arg + config.set_notfound_view(view) + request = self._makeRequest(config) + view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, (None, request)) + + def test_set_notfound_view_request_has_context(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.exceptions import NotFound + config = self._makeOne(autocommit=True) + view = lambda *arg: arg + config.set_notfound_view(view) + request = self._makeRequest(config) + request.context = 'abc' + view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, ('abc', request)) + + def test_set_forbidden_view(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.exceptions import Forbidden + config = self._makeOne(autocommit=True) + view = lambda *arg: 'OK' + config.set_forbidden_view(view) + request = self._makeRequest(config) + view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, 'OK') + + def test_set_forbidden_view_request_has_context(self): + from zope.interface import implementedBy + from pyramid.interfaces import IRequest + from pyramid.exceptions import Forbidden + config = self._makeOne(autocommit=True) + view = lambda *arg: arg + config.set_forbidden_view(view) + request = self._makeRequest(config) + request.context = 'abc' + view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), + request_iface=IRequest) + result = view(None, request) + self.assertEqual(result, ('abc', request)) + + def test__set_authentication_policy(self): + from pyramid.interfaces import IAuthenticationPolicy + config = self._makeOne(autocommit=True) + policy = object() + config._set_authentication_policy(policy) + self.assertEqual( + config.registry.getUtility(IAuthenticationPolicy), policy) + + def test__set_authorization_policy(self): + from pyramid.interfaces import IAuthorizationPolicy + config = self._makeOne(autocommit=True) + policy = object() + config._set_authorization_policy(policy) + self.assertEqual( + config.registry.getUtility(IAuthorizationPolicy), policy) + + def test_set_locale_negotiator(self): + from pyramid.interfaces import ILocaleNegotiator + config = self._makeOne(autocommit=True) + def negotiator(request): pass + config.set_locale_negotiator(negotiator) + self.assertEqual(config.registry.getUtility(ILocaleNegotiator), + negotiator) + + def test_set_locale_negotiator_dottedname(self): + from pyramid.interfaces import ILocaleNegotiator + config = self._makeOne(autocommit=True) + config.set_locale_negotiator( + 'pyramid.tests.test_config.dummyfactory') + self.assertEqual(config.registry.getUtility(ILocaleNegotiator), + dummyfactory) + + def test_set_request_factory(self): + from pyramid.interfaces import IRequestFactory + config = self._makeOne(autocommit=True) + factory = object() + config.set_request_factory(factory) + self.assertEqual(config.registry.getUtility(IRequestFactory), factory) + + def test_set_request_factory_dottedname(self): + from pyramid.interfaces import IRequestFactory + config = self._makeOne(autocommit=True) + config.set_request_factory( + 'pyramid.tests.test_config.dummyfactory') + self.assertEqual(config.registry.getUtility(IRequestFactory), + dummyfactory) + + def test_set_renderer_globals_factory(self): + from pyramid.interfaces import IRendererGlobalsFactory + config = self._makeOne(autocommit=True) + factory = object() + config.set_renderer_globals_factory(factory) + self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), + factory) + + def test_set_renderer_globals_factory_dottedname(self): + from pyramid.interfaces import IRendererGlobalsFactory + config = self._makeOne(autocommit=True) + config.set_renderer_globals_factory( + 'pyramid.tests.test_config.dummyfactory') + self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), + dummyfactory) + + def test_set_default_permission(self): + from pyramid.interfaces import IDefaultPermission + config = self._makeOne(autocommit=True) + config.set_default_permission('view') + self.assertEqual(config.registry.getUtility(IDefaultPermission), + 'view') + + def test_set_session_factory(self): + from pyramid.interfaces import ISessionFactory + config = self._makeOne(autocommit=True) + config.set_session_factory('factory') + self.assertEqual(config.registry.getUtility(ISessionFactory), + 'factory') + + def test_add_translation_dirs_missing_dir(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, + config.add_translation_dirs, + '/wont/exist/on/my/system') + + def test_add_translation_dirs_resource_spec(self): + import os + from pyramid.interfaces import ITranslationDirectories + config = self._makeOne(autocommit=True) + config.add_translation_dirs('pyramid.tests.localeapp:locale') + here = os.path.dirname(__file__) + locale = os.path.join(here, 'localeapp', 'locale') + self.assertEqual(config.registry.getUtility(ITranslationDirectories), + [locale]) + + def test_add_translation_dirs_registers_chameleon_translate(self): + from pyramid.interfaces import IChameleonTranslate + from pyramid.threadlocal import manager + request = DummyRequest() + config = self._makeOne(autocommit=True) + manager.push({'request':request, 'registry':config.registry}) + try: + config.add_translation_dirs('pyramid.tests.localeapp:locale') + translate = config.registry.getUtility(IChameleonTranslate) + self.assertEqual(translate('Approve'), u'Approve') + finally: + manager.pop() + + def test_add_translation_dirs_abspath(self): + import os + from pyramid.interfaces import ITranslationDirectories + config = self._makeOne(autocommit=True) + here = os.path.dirname(__file__) + locale = os.path.join(here, 'localeapp', 'locale') + config.add_translation_dirs(locale) + self.assertEqual(config.registry.getUtility(ITranslationDirectories), + [locale]) + + def test_derive_view_function(self): + def view(request): + return 'OK' + config = self._makeOne() + result = config.derive_view(view) + self.failIf(result is view) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_dottedname(self): + config = self._makeOne() + result = config.derive_view( + 'pyramid.tests.test_config.dummy_view') + self.failIf(result is dummy_view) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_with_renderer(self): + def view(request): + return 'OK' + config = self._makeOne(autocommit=True) + class moo(object): + def __init__(self, *arg, **kw): + pass + def __call__(self, *arg, **kw): + return 'moo' + config.add_renderer('moo', moo) + result = config.derive_view(view, renderer='moo') + self.failIf(result is view) + self.assertEqual(result(None, None).body, 'moo') + + def test_derive_view_with_default_renderer_no_explicit_renderer(self): + def view(request): + return 'OK' + config = self._makeOne(autocommit=True) + class moo(object): + def __init__(self, *arg, **kw): + pass + def __call__(self, *arg, **kw): + return 'moo' + config.add_renderer(None, moo) + result = config.derive_view(view) + self.failIf(result is view) + self.assertEqual(result(None, None).body, 'moo') + + def test_derive_view_with_default_renderer_with_explicit_renderer(self): + def view(request): + return 'OK' + config = self._makeOne(autocommit=True) + class moo(object): pass + class foo(object): + def __init__(self, *arg, **kw): + pass + def __call__(self, *arg, **kw): + return 'foo' + config.add_renderer(None, moo) + config.add_renderer('foo', foo) + result = config.derive_view(view, renderer='foo') + self.failIf(result is view) + self.assertEqual(result(None, None).body, 'foo') + + def test_derive_view_class_without_attr(self): + class View(object): + def __init__(self, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config.derive_view(View) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_class_with_attr(self): + class View(object): + def __init__(self, request): + pass + def another(self): + return 'OK' + config = self._makeOne() + result = config.derive_view(View, attr='another') + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_function_context_and_request(self): + def view(context, request): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failUnless(result is view) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(view(None, None), 'OK') + + def test__derive_view_as_function_requestonly(self): + def view(request): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_newstyle_class_context_and_request(self): + class view(object): + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_newstyle_class_requestonly(self): + class view(object): + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_oldstyle_class_context_and_request(self): + class view: + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_instance_context_and_request(self): + class View: + def __call__(self, context, request): + return 'OK' + view = View() + config = self._makeOne() + result = config._derive_view(view) + self.failUnless(result is view) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_as_instance_requestonly(self): + class View: + def __call__(self, request): + return 'OK' + view = View() + config = self._makeOne() + result = config._derive_view(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') + + def test__derive_view_with_debug_authorization_no_authpol(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + logger = self._registerLogger(config) + result = config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + self.assertEqual(len(logger.messages), 1) + self.assertEqual(logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): Allowed " + "(no authorization policy in use)") + + def test__derive_view_with_debug_authorization_no_permission(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + self._registerSecurityPolicy(config, True) + logger = self._registerLogger(config) + result = config._derive_view(view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + self.assertEqual(len(logger.messages), 1) + self.assertEqual(logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): Allowed (" + "no permission registered)") + + def test__derive_view_debug_auth_permission_authpol_permitted(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, debug_authorization=True, + reload_templates=True) + logger = self._registerLogger(config) + self._registerSecurityPolicy(config, True) + result = config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__call_permissive__, view) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + self.assertEqual(len(logger.messages), 1) + self.assertEqual(logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): True") + + def test__derive_view_debug_auth_permission_authpol_denied(self): + from pyramid.exceptions import Forbidden + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + logger = self._registerLogger(config) + self._registerSecurityPolicy(config, False) + result = config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__call_permissive__, view) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertRaises(Forbidden, result, None, request) + self.assertEqual(len(logger.messages), 1) + self.assertEqual(logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): False") + + def test__derive_view_debug_auth_permission_authpol_denied2(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + self._registerLogger(config) + self._registerSecurityPolicy(config, False) + result = config._derive_view(view, permission='view') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + permitted = result.__permitted__(None, None) + self.assertEqual(permitted, False) + + def test__derive_view_debug_auth_permission_authpol_overridden(self): + view = lambda *arg: 'OK' + config = self._makeOne() + self._registerSettings(config, + debug_authorization=True, reload_templates=True) + logger = self._registerLogger(config) + self._registerSecurityPolicy(config, False) + result = config._derive_view(view, + permission='__no_permission_required__') + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest(config) + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + self.assertEqual(len(logger.messages), 1) + self.assertEqual(logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): False") + + def test__derive_view_with_predicates_all(self): + view = lambda *arg: 'OK' + predicates = [] + def predicate1(context, request): + predicates.append(True) + return True + def predicate2(context, request): + predicates.append(True) + return True + config = self._makeOne() + result = config._derive_view(view, predicates=[predicate1, predicate2]) + request = self._makeRequest(config) + request.method = 'POST' + next = result(None, None) + self.assertEqual(next, 'OK') + self.assertEqual(predicates, [True, True]) + + def test__derive_view_with_predicates_checker(self): + view = lambda *arg: 'OK' + predicates = [] + def predicate1(context, request): + predicates.append(True) + return True + def predicate2(context, request): + predicates.append(True) + return True + config = self._makeOne() + result = config._derive_view(view, predicates=[predicate1, predicate2]) + request = self._makeRequest(config) + request.method = 'POST' + next = result.__predicated__(None, None) + self.assertEqual(next, True) + self.assertEqual(predicates, [True, True]) + + def test__derive_view_with_predicates_notall(self): + from pyramid.exceptions import NotFound + view = lambda *arg: 'OK' + predicates = [] + def predicate1(context, request): + predicates.append(True) + return True + def predicate2(context, request): + predicates.append(True) + return False + config = self._makeOne() + result = config._derive_view(view, predicates=[predicate1, predicate2]) + request = self._makeRequest(config) + request.method = 'POST' + self.assertRaises(NotFound, result, None, None) + self.assertEqual(predicates, [True, True]) + + def test__derive_view_with_wrapper_viewname(self): + from webob import Response + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + inner_response = Response('OK') + def inner_view(context, request): + return inner_response + def outer_view(context, request): + self.assertEqual(request.wrapped_response, inner_response) + self.assertEqual(request.wrapped_body, inner_response.body) + self.assertEqual(request.wrapped_view, inner_view) + return Response('outer ' + request.wrapped_body) + config = self._makeOne() + config.registry.registerAdapter( + outer_view, (IViewClassifier, None, None), IView, 'owrap') + result = config._derive_view(inner_view, viewname='inner', + wrapper_viewname='owrap') + self.failIf(result is inner_view) + self.assertEqual(inner_view.__module__, result.__module__) + self.assertEqual(inner_view.__doc__, result.__doc__) + request = self._makeRequest(config) + request.registry = config.registry + response = result(None, request) + self.assertEqual(response.body, 'outer OK') + + def test__derive_view_with_wrapper_viewname_notfound(self): + from webob import Response + inner_response = Response('OK') + def inner_view(context, request): + return inner_response + config = self._makeOne() + request = self._makeRequest(config) + request.registry = config.registry + wrapped = config._derive_view( + inner_view, viewname='inner', wrapper_viewname='owrap') + self.assertRaises(ValueError, wrapped, None, request) + + def test_override_resource_samename(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_resource,'a', 'a') + + def test_override_resource_directory_with_file(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_resource, + 'a:foo/', 'a:foo.pt') + + def test_override_resource_file_with_directory(self): + from pyramid.exceptions import ConfigurationError + config = self._makeOne() + self.assertRaises(ConfigurationError, config.override_resource, + 'a:foo.pt', 'a:foo/') + + def test_override_resource_success(self): + config = self._makeOne(autocommit=True) + override = DummyUnderOverride() + config.override_resource( + 'pyramid.tests.fixtureapp:templates/foo.pt', + 'pyramid.tests.fixtureapp.subpackage:templates/bar.pt', + _override=override) + from pyramid.tests import fixtureapp + from pyramid.tests.fixtureapp import subpackage + self.assertEqual(override.package, fixtureapp) + self.assertEqual(override.path, 'templates/foo.pt') + self.assertEqual(override.override_package, subpackage) + self.assertEqual(override.override_prefix, 'templates/bar.pt') + + def test_add_renderer(self): + from pyramid.interfaces import IRendererFactory + config = self._makeOne(autocommit=True) + renderer = object() + config.add_renderer('name', renderer) + self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), + renderer) + + def test_add_renderer_dottedname_factory(self): + from pyramid.interfaces import IRendererFactory + config = self._makeOne(autocommit=True) + import pyramid.tests + config.add_renderer('name', 'pyramid.tests') + self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), + pyramid.tests) + + def test_scan_integration(self): + import os + from zope.interface import alsoProvides + from pyramid.interfaces import IRequest + from pyramid.view import render_view_to_response + import pyramid.tests.grokkedapp as package + config = self._makeOne(autocommit=True) + config.scan(package) + + ctx = DummyContext() + req = DummyRequest() + alsoProvides(req, IRequest) + req.registry = config.registry + + req.method = 'GET' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked') + + req.method = 'POST' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked_post') + + result= render_view_to_response(ctx, req, 'grokked_class') + self.assertEqual(result, 'grokked_class') + + result= render_view_to_response(ctx, req, 'grokked_instance') + self.assertEqual(result, 'grokked_instance') + + result= render_view_to_response(ctx, req, 'oldstyle_grokked_class') + self.assertEqual(result, 'oldstyle_grokked_class') + + req.method = 'GET' + result = render_view_to_response(ctx, req, 'another') + self.assertEqual(result, 'another_grokked') + + req.method = 'POST' + result = render_view_to_response(ctx, req, 'another') + self.assertEqual(result, 'another_grokked_post') + + result= render_view_to_response(ctx, req, 'another_grokked_class') + self.assertEqual(result, 'another_grokked_class') + + result= render_view_to_response(ctx, req, 'another_grokked_instance') + self.assertEqual(result, 'another_grokked_instance') + + result= render_view_to_response(ctx, req, + 'another_oldstyle_grokked_class') + self.assertEqual(result, 'another_oldstyle_grokked_class') + + result = render_view_to_response(ctx, req, 'stacked1') + self.assertEqual(result, 'stacked') + + result = render_view_to_response(ctx, req, 'stacked2') + self.assertEqual(result, 'stacked') + + result = render_view_to_response(ctx, req, 'another_stacked1') + self.assertEqual(result, 'another_stacked') + + result = render_view_to_response(ctx, req, 'another_stacked2') + self.assertEqual(result, 'another_stacked') + + result = render_view_to_response(ctx, req, 'stacked_class1') + self.assertEqual(result, 'stacked_class') + + result = render_view_to_response(ctx, req, 'stacked_class2') + self.assertEqual(result, 'stacked_class') + + result = render_view_to_response(ctx, req, 'another_stacked_class1') + self.assertEqual(result, 'another_stacked_class') + + result = render_view_to_response(ctx, req, 'another_stacked_class2') + self.assertEqual(result, 'another_stacked_class') + + if not os.name.startswith('java'): + # on Jython, a class without an __init__ apparently accepts + # any number of arguments without raising a TypeError. + + self.assertRaises(TypeError, + render_view_to_response, ctx, req, 'basemethod') + + result = render_view_to_response(ctx, req, 'method1') + self.assertEqual(result, 'method1') + + result = render_view_to_response(ctx, req, 'method2') + self.assertEqual(result, 'method2') + + result = render_view_to_response(ctx, req, 'stacked_method1') + self.assertEqual(result, 'stacked_method') + + result = render_view_to_response(ctx, req, 'stacked_method2') + self.assertEqual(result, 'stacked_method') + + result = render_view_to_response(ctx, req, 'subpackage_init') + self.assertEqual(result, 'subpackage_init') + + result = render_view_to_response(ctx, req, 'subpackage_notinit') + self.assertEqual(result, 'subpackage_notinit') + + result = render_view_to_response(ctx, req, 'subsubpackage_init') + self.assertEqual(result, 'subsubpackage_init') + + result = render_view_to_response(ctx, req, 'pod_notinit') + self.assertEqual(result, None) + + def test_scan_integration_dottedname_package(self): + from zope.interface import alsoProvides + from pyramid.interfaces import IRequest + from pyramid.view import render_view_to_response + config = self._makeOne(autocommit=True) + config.scan('pyramid.tests.grokkedapp') + + ctx = DummyContext() + req = DummyRequest() + alsoProvides(req, IRequest) + req.registry = config.registry + + req.method = 'GET' + result = render_view_to_response(ctx, req, '') + self.assertEqual(result, 'grokked') + + def test_testing_securitypolicy(self): + from pyramid.testing import DummySecurityPolicy + config = self._makeOne(autocommit=True) + config.testing_securitypolicy('user', ('group1', 'group2'), + permissive=False) + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + ut = config.registry.getUtility(IAuthenticationPolicy) + self.failUnless(isinstance(ut, DummySecurityPolicy)) + ut = config.registry.getUtility(IAuthorizationPolicy) + self.assertEqual(ut.userid, 'user') + self.assertEqual(ut.groupids, ('group1', 'group2')) + self.assertEqual(ut.permissive, False) + + def test_testing_models(self): + from pyramid.traversal import find_model + from pyramid.interfaces import ITraverser + ob1 = object() + ob2 = object() + models = {'/ob1':ob1, '/ob2':ob2} + config = self._makeOne(autocommit=True) + config.testing_models(models) + adapter = config.registry.getAdapter(None, ITraverser) + result = adapter({'PATH_INFO':'/ob1'}) + self.assertEqual(result['context'], ob1) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (u'ob1',)) + self.assertEqual(result['virtual_root'], ob1) + self.assertEqual(result['virtual_root_path'], ()) + result = adapter({'PATH_INFO':'/ob2'}) + self.assertEqual(result['context'], ob2) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (u'ob2',)) + self.assertEqual(result['virtual_root'], ob2) + self.assertEqual(result['virtual_root_path'], ()) + self.assertRaises(KeyError, adapter, {'PATH_INFO':'/ob3'}) + try: + config.begin() + self.assertEqual(find_model(None, '/ob1'), ob1) + finally: + config.end() + + def test_testing_add_subscriber_single(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber(IDummy) + event = DummyEvent() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_testing_add_subscriber_dottedname(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber( + 'pyramid.tests.test_config.IDummy') + event = DummyEvent() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_testing_add_subscriber_multiple(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber((Interface, IDummy)) + event = DummyEvent() + event.object = 'foo' + # the below is the equivalent of z.c.event.objectEventNotify(event) + config.registry.subscribers((event.object, event), None) + self.assertEqual(len(L), 2) + self.assertEqual(L[0], 'foo') + self.assertEqual(L[1], event) + + def test_testing_add_subscriber_defaults(self): + config = self._makeOne(autocommit=True) + L = config.testing_add_subscriber() + event = object() + config.registry.notify(event) + self.assertEqual(L[-1], event) + event2 = object() + config.registry.notify(event2) + self.assertEqual(L[-1], event2) + + def test_hook_zca(self): + from pyramid.threadlocal import get_current_registry + gsm = DummyGetSiteManager() + config = self._makeOne() + config.hook_zca(getSiteManager=gsm) + self.assertEqual(gsm.hook, get_current_registry) + + def test_unhook_zca(self): + gsm = DummyGetSiteManager() + config = self._makeOne() + config.unhook_zca(getSiteManager=gsm) + self.assertEqual(gsm.unhooked, True) + + def test_testing_add_renderer(self): + config = self._makeOne(autocommit=True) + renderer = config.testing_add_renderer('templates/foo.pt') + from pyramid.testing import DummyTemplateRenderer + self.failUnless(isinstance(renderer, 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) + renderer.assert_(foo=1) + renderer.assert_(bar=2) + renderer.assert_(request=request) + + def test_testing_add_renderer_explicitrenderer(self): + config = self._makeOne(autocommit=True) + class E(Exception): pass + def renderer(kw, system): + self.assertEqual(kw, {'foo':1, 'bar':2}) + raise E + renderer = config.testing_add_renderer('templates/foo.pt', renderer) + from pyramid.renderers import render_to_response + # must provide request to pass in registry (this is a functest) + request = DummyRequest() + request.registry = config.registry + try: + render_to_response( + 'templates/foo.pt', {'foo':1, 'bar':2}, request=request) + except E: + pass + else: # pragma: no cover + raise AssertionError + + def test_testing_add_template(self): + config = self._makeOne(autocommit=True) + renderer = config.testing_add_template('templates/foo.pt') + from pyramid.testing import DummyTemplateRenderer + self.failUnless(isinstance(renderer, 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', dict(foo=1, bar=2), + request=request) + renderer.assert_(foo=1) + renderer.assert_(bar=2) + renderer.assert_(request=request) + + def test_commit_conflict_simple(self): + from zope.configuration.config import ConfigurationConflictError + config = self._makeOne() + def view1(request): pass + def view2(request): pass + config.add_view(view1) + config.add_view(view2) + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_commit_conflict_resolved_with_include(self): + config = self._makeOne() + def view1(request): pass + def view2(request): pass + def includeme(config): + config.add_view(view2) + config.add_view(view1) + config.include(includeme) + config.commit() + registeredview = self._getViewCallable(config) + self.assertEqual(registeredview.__name__, 'view1') + + def test_commit_conflict_with_two_includes(self): + from zope.configuration.config import ConfigurationConflictError + config = self._makeOne() + def view1(request): pass + def view2(request): pass + def includeme1(config): + config.add_view(view1) + def includeme2(config): + config.add_view(view2) + config.include(includeme1) + config.include(includeme2) + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_commit_conflict_resolved_with_two_includes_and_local(self): + config = self._makeOne() + def view1(request): pass + def view2(request): pass + def view3(request): pass + def includeme1(config): + config.add_view(view1) + def includeme2(config): + config.add_view(view2) + config.include(includeme1) + config.include(includeme2) + config.add_view(view3) + config.commit() + registeredview = self._getViewCallable(config) + self.assertEqual(registeredview.__name__, 'view3') + + def test_autocommit_no_conflicts(self): + config = self._makeOne(autocommit=True) + def view1(request): pass + def view2(request): pass + def view3(request): pass + config.add_view(view1) + config.add_view(view2) + config.add_view(view3) + config.commit() + registeredview = self._getViewCallable(config) + self.assertEqual(registeredview.__name__, 'view3') + +class Test__map_view(unittest.TestCase): + def setUp(self): + from pyramid.registry import Registry + self.registry = Registry() + testing.setUp(registry=self.registry) + + def tearDown(self): + del self.registry + testing.tearDown() + + def _registerRenderer(self, typ='.txt'): + from pyramid.interfaces import IRendererFactory + from pyramid.interfaces import ITemplateRenderer + from zope.interface import implements + class Renderer: + implements(ITemplateRenderer) + spec = 'abc' + typ + def __init__(self, path): + self.__class__.path = path + def __call__(self, *arg): + return 'Hello!' + self.registry.registerUtility(Renderer, IRendererFactory, name=typ) + return Renderer + + def _makeRequest(self): + request = DummyRequest() + request.registry = self.registry + return request + + def _callFUT(self, view, **kw): + from pyramid.config import _map_view + return _map_view(view, self.registry, **kw) + + def test__map_view_as_function_context_and_request(self): + def view(context, request): + return 'OK' + result = self._callFUT(view) + self.failUnless(result is view) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_function_with_attr(self): + def view(context, request): + """ """ + result = self._callFUT(view, attr='__name__') + self.failIf(result is view) + self.assertRaises(TypeError, result, None, None) + + def test__map_view_as_function_with_attr_and_renderer(self): + renderer = self._registerRenderer() + view = lambda *arg: 'OK' + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='__name__', renderer=info) + self.failIf(result is view) + self.assertRaises(TypeError, result, None, None) + + def test__map_view_as_function_requestonly(self): + def view(request): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_function_requestonly_with_attr(self): + def view(request): + """ """ + result = self._callFUT(view, attr='__name__') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertRaises(TypeError, result, None, None) + + def test__map_view_as_newstyle_class_context_and_request(self): + class view(object): + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_context_and_request_with_attr(self): + class view(object): + def __init__(self, context, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_context_and_request_attr_and_renderer( + self): + renderer = self._registerRenderer() + class view(object): + def __init__(self, context, request): + pass + def index(self): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_newstyle_class_requestonly(self): + class view(object): + def __init__(self, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_requestonly_with_attr(self): + class view(object): + def __init__(self, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_newstyle_class_requestonly_attr_and_renderer(self): + renderer = self._registerRenderer() + class view(object): + def __init__(self, request): + pass + def index(self): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_oldstyle_class_context_and_request(self): + class view: + def __init__(self, context, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_class_context_and_request_with_attr(self): + class view: + def __init__(self, context, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_cls_context_request_attr_and_renderer(self): + renderer = self._registerRenderer() + class view: + def __init__(self, context, request): + pass + def index(self): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, request): + pass + def __call__(self): + return 'OK' + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_class_requestonly_with_attr(self): + class view: + def __init__(self, request): + pass + def index(self): + return 'OK' + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_oldstyle_class_requestonly_attr_and_renderer(self): + renderer = self._registerRenderer() + class view: + def __init__(self, request): + pass + def index(self): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_instance_context_and_request(self): + class View: + def __call__(self, context, request): + return 'OK' + view = View() + result = self._callFUT(view) + self.failUnless(result is view) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_context_and_request_and_attr(self): + class View: + def index(self, context, request): + return 'OK' + view = View() + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_context_and_request_attr_and_renderer(self): + renderer = self._registerRenderer() + class View: + def index(self, context, request): + return {'a':'1'} + view = View() + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_as_instance_requestonly(self): + class View: + def __call__(self, request): + return 'OK' + view = View() + result = self._callFUT(view) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_requestonly_with_attr(self): + class View: + def index(self, request): + return 'OK' + view = View() + result = self._callFUT(view, attr='index') + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + self.assertEqual(result(None, None), 'OK') + + def test__map_view_as_instance_requestonly_with_attr_and_renderer(self): + renderer = self._registerRenderer() + class View: + def index(self, request): + return {'a':'1'} + view = View() + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.failUnless('instance' in result.__name__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_rendereronly(self): + renderer = self._registerRenderer() + def view(context, request): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + + def test__map_view_with_registry(self): + renderer = self._registerRenderer() + def view(context, request): + return {'a':'1'} + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, renderer=info) + self.failIf(result is view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + request = self._makeRequest() + self.assertEqual(result(None, request).body, 'Hello!') + +class Test_decorate_view(unittest.TestCase): + def _callFUT(self, wrapped, original): + from pyramid.config import decorate_view + return decorate_view(wrapped, original) + + def test_it_same(self): + def view(context, request): + """ """ + result = self._callFUT(view, view) + self.assertEqual(result, False) + + def test_it_different(self): + class DummyView1: + """ 1 """ + __name__ = '1' + __module__ = '1' + def __call__(self, context, request): + """ """ + def __call_permissive__(self, context, reuqest): + """ """ + def __predicated__(self, context, reuqest): + """ """ + def __permitted__(self, context, request): + """ """ + class DummyView2: + """ 2 """ + __name__ = '2' + __module__ = '2' + def __call__(self, context, request): + """ """ + def __call_permissive__(self, context, reuqest): + """ """ + def __predicated__(self, context, reuqest): + """ """ + def __permitted__(self, context, request): + """ """ + view1 = DummyView1() + view2 = DummyView2() + result = self._callFUT(view1, view2) + self.assertEqual(result, True) + self.failUnless(view1.__doc__ is view2.__doc__) + self.failUnless(view1.__module__ is view2.__module__) + self.failUnless(view1.__name__ is view2.__name__) + self.failUnless(view1.__call_permissive__.im_func is + view2.__call_permissive__.im_func) + self.failUnless(view1.__permitted__.im_func is + view2.__permitted__.im_func) + self.failUnless(view1.__predicated__.im_func is + view2.__predicated__.im_func) + +class Test__make_predicates(unittest.TestCase): + def _callFUT(self, **kw): + from pyramid.config import _make_predicates + return _make_predicates(**kw) + + def test_ordering_xhr_and_request_method_trump_only_containment(self): + order1, _, _ = self._callFUT(xhr=True, request_method='GET') + order2, _, _ = self._callFUT(containment=True) + self.failUnless(order1 < order2) + + def test_ordering_number_of_predicates(self): + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + accept='accept', + containment='containment', + request_type='request_type', + custom=('a',) + ) + order2, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + accept='accept', + containment='containment', + request_type='request_type', + custom=('a',) + ) + order3, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + accept='accept', + containment='containment', + request_type='request_type', + ) + order4, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + accept='accept', + containment='containment', + ) + order5, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + accept='accept', + ) + order6, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + header='header', + ) + order7, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + request_param='param', + ) + order8, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + ) + order9, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + ) + order10, _, _ = self._callFUT( + xhr='xhr', + ) + order11, _, _ = self._callFUT( + ) + self.assertEqual(order1, order2) + self.failUnless(order3 > order2) + self.failUnless(order4 > order3) + self.failUnless(order5 > order4) + self.failUnless(order6 > order5) + self.failUnless(order7 > order6) + self.failUnless(order8 > order7) + self.failUnless(order9 > order8) + self.failUnless(order10 > order9) + self.failUnless(order11 > order10) + + def test_ordering_importance_of_predicates(self): + order1, _, _ = self._callFUT( + xhr='xhr', + ) + order2, _, _ = self._callFUT( + request_method='request_method', + ) + order3, _, _ = self._callFUT( + path_info='path_info', + ) + order4, _, _ = self._callFUT( + request_param='param', + ) + order5, _, _ = self._callFUT( + header='header', + ) + order6, _, _ = self._callFUT( + accept='accept', + ) + order7, _, _ = self._callFUT( + containment='containment', + ) + order8, _, _ = self._callFUT( + request_type='request_type', + ) + order9, _, _ = self._callFUT( + custom=('a',), + ) + self.failUnless(order1 > order2) + self.failUnless(order2 > order3) + self.failUnless(order3 > order4) + self.failUnless(order4 > order5) + self.failUnless(order5 > order6) + self.failUnless(order6 > order7) + self.failUnless(order7 > order8) + self.failUnless(order8 > order9) + + def test_ordering_importance_and_number(self): + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + ) + order2, _, _ = self._callFUT( + custom=('a',), + ) + self.failUnless(order1 < order2) + + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + ) + order2, _, _ = self._callFUT( + request_method='request_method', + custom=('a',), + ) + self.failUnless(order1 > order2) + + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + ) + order2, _, _ = self._callFUT( + request_method='request_method', + custom=('a',), + ) + self.failUnless(order1 < order2) + + order1, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + path_info='path_info', + ) + order2, _, _ = self._callFUT( + xhr='xhr', + request_method='request_method', + custom=('a',), + ) + self.failUnless(order1 > order2) + + def test_different_custom_predicates_with_same_hash(self): + class PredicateWithHash(object): + def __hash__(self): + return 1 + a = PredicateWithHash() + b = PredicateWithHash() + _, _, a_phash = self._callFUT(custom=(a,)) + _, _, b_phash = self._callFUT(custom=(b,)) + self.assertEqual(a_phash, b_phash) + + def test_traverse_has_remainder_already(self): + order, predicates, phash = self._callFUT(traverse='/1/:a/:b') + self.assertEqual(len(predicates), 1) + pred = predicates[0] + info = {'traverse':'abc'} + request = DummyRequest() + result = pred(info, request) + self.assertEqual(result, True) + self.assertEqual(info, {'traverse':'abc'}) + + def test_traverse_matches(self): + order, predicates, phash = self._callFUT(traverse='/1/:a/:b') + self.assertEqual(len(predicates), 1) + pred = predicates[0] + info = {'match':{'a':'a', 'b':'b'}} + request = DummyRequest() + result = pred(info, request) + self.assertEqual(result, True) + self.assertEqual(info, {'match': + {'a':'a', 'b':'b', 'traverse':('1', 'a', 'b')}}) + +class TestMultiView(unittest.TestCase): + def _getTargetClass(self): + from pyramid.config import MultiView + return MultiView + + def _makeOne(self, name='name'): + return self._getTargetClass()(name) + + def test_class_implements_ISecuredView(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import ISecuredView + verifyClass(ISecuredView, self._getTargetClass()) + + def test_instance_implements_ISecuredView(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import ISecuredView + verifyObject(ISecuredView, self._makeOne()) + + def test_add(self): + mv = self._makeOne() + mv.add('view', 100) + self.assertEqual(mv.views, [(100, 'view', None)]) + mv.add('view2', 99) + self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)]) + mv.add('view3', 100, 'text/html') + self.assertEqual(mv.media_views['text/html'], [(100, 'view3', None)]) + mv.add('view4', 99, 'text/html') + self.assertEqual(mv.media_views['text/html'], + [(99, 'view4', None), (100, 'view3', None)]) + mv.add('view5', 100, 'text/xml') + self.assertEqual(mv.media_views['text/xml'], [(100, 'view5', None)]) + self.assertEqual(set(mv.accepts), set(['text/xml', 'text/html'])) + self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)]) + mv.add('view6', 98, 'text/*') + self.assertEqual(mv.views, [(98, 'view6', None), + (99, 'view2', None), + (100, 'view', None)]) + + def test_add_with_phash(self): + mv = self._makeOne() + mv.add('view', 100, phash='abc') + self.assertEqual(mv.views, [(100, 'view', 'abc')]) + mv.add('view', 100, phash='abc') + self.assertEqual(mv.views, [(100, 'view', 'abc')]) + mv.add('view', 100, phash='def') + self.assertEqual(mv.views, [(100, 'view', 'abc'), (100, 'view', 'def')]) + mv.add('view', 100, phash='abc') + self.assertEqual(mv.views, [(100, 'view', 'abc'), (100, 'view', 'def')]) + + def test_get_views_request_has_no_accept(self): + request = DummyRequest() + mv = self._makeOne() + mv.views = [(99, lambda *arg: None)] + self.assertEqual(mv.get_views(request), mv.views) + + def test_get_views_no_self_accepts(self): + request = DummyRequest() + request.accept = True + mv = self._makeOne() + mv.accepts = [] + mv.views = [(99, lambda *arg: None)] + self.assertEqual(mv.get_views(request), mv.views) + + def test_get_views(self): + request = DummyRequest() + request.accept = DummyAccept('text/html') + mv = self._makeOne() + mv.accepts = ['text/html'] + mv.views = [(99, lambda *arg: None)] + html_views = [(98, lambda *arg: None)] + mv.media_views['text/html'] = html_views + self.assertEqual(mv.get_views(request), html_views + mv.views) + + def test_get_views_best_match_returns_None(self): + request = DummyRequest() + request.accept = DummyAccept(None) + mv = self._makeOne() + mv.accepts = ['text/html'] + mv.views = [(99, lambda *arg: None)] + self.assertEqual(mv.get_views(request), mv.views) + + def test_match_not_found(self): + from pyramid.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, mv.match, context, request) + + def test_match_predicate_fails(self): + from pyramid.exceptions import NotFound + mv = self._makeOne() + def view(context, request): + """ """ + view.__predicated__ = lambda *arg: False + mv.views = [(100, view, None)] + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, mv.match, context, request) + + def test_match_predicate_succeeds(self): + mv = self._makeOne() + def view(context, request): + """ """ + view.__predicated__ = lambda *arg: True + mv.views = [(100, view, None)] + context = DummyContext() + request = DummyRequest() + result = mv.match(context, request) + self.assertEqual(result, view) + + def test_permitted_no_views(self): + from pyramid.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, mv.__permitted__, context, request) + + def test_permitted_no_match_with__permitted__(self): + mv = self._makeOne() + def view(context, request): + """ """ + mv.views = [(100, view, None)] + self.assertEqual(mv.__permitted__(None, None), True) + + def test_permitted(self): + mv = self._makeOne() + def view(context, request): + """ """ + def permitted(context, request): + return False + view.__permitted__ = permitted + mv.views = [(100, view, None)] + context = DummyContext() + request = DummyRequest() + result = mv.__permitted__(context, request) + self.assertEqual(result, False) + + def test__call__not_found(self): + from pyramid.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, mv, context, request) + + def test___call__intermediate_not_found(self): + from pyramid.exceptions import PredicateMismatch + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + expected_response = DummyResponse() + def view1(context, request): + raise PredicateMismatch + def view2(context, request): + return expected_response + mv.views = [(100, view1, None), (99, view2, None)] + response = mv(context, request) + self.assertEqual(response, expected_response) + + def test___call__raise_not_found_isnt_interpreted_as_pred_mismatch(self): + from pyramid.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + def view1(context, request): + raise NotFound + def view2(context, request): + """ """ + mv.views = [(100, view1, None), (99, view2, None)] + self.assertRaises(NotFound, mv, context, request) + + def test___call__(self): + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + expected_response = DummyResponse() + def view(context, request): + return expected_response + mv.views = [(100, view, None)] + response = mv(context, request) + self.assertEqual(response, expected_response) + + def test__call_permissive__not_found(self): + from pyramid.exceptions import NotFound + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + self.assertRaises(NotFound, mv, context, request) + + def test___call_permissive_has_call_permissive(self): + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + expected_response = DummyResponse() + def view(context, request): + """ """ + def permissive(context, request): + return expected_response + view.__call_permissive__ = permissive + mv.views = [(100, view, None)] + response = mv.__call_permissive__(context, request) + self.assertEqual(response, expected_response) + + def test___call_permissive_has_no_call_permissive(self): + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.view_name = '' + expected_response = DummyResponse() + def view(context, request): + return expected_response + mv.views = [(100, view, None)] + response = mv.__call_permissive__(context, request) + self.assertEqual(response, expected_response) + + def test__call__with_accept_match(self): + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.accept = DummyAccept('text/html', 'text/xml') + expected_response = DummyResponse() + def view(context, request): + return expected_response + mv.views = [(100, None)] + mv.media_views['text/xml'] = [(100, view, None)] + mv.accepts = ['text/xml'] + response = mv(context, request) + self.assertEqual(response, expected_response) + + def test__call__with_accept_miss(self): + mv = self._makeOne() + context = DummyContext() + request = DummyRequest() + request.accept = DummyAccept('text/plain', 'text/html') + expected_response = DummyResponse() + def view(context, request): + return expected_response + mv.views = [(100, view, None)] + mv.media_views['text/xml'] = [(100, None, None)] + mv.accepts = ['text/xml'] + response = mv(context, request) + self.assertEqual(response, expected_response) + + +class TestRequestOnly(unittest.TestCase): + def _callFUT(self, arg): + from pyramid.config import requestonly + return requestonly(arg) + + def test_newstyle_class_no_init(self): + class foo(object): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_newstyle_class_init_toomanyargs(self): + class foo(object): + def __init__(self, context, request): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_newstyle_class_init_onearg_named_request(self): + class foo(object): + def __init__(self, request): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_newstyle_class_init_onearg_named_somethingelse(self): + class foo(object): + def __init__(self, req): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_newstyle_class_init_defaultargs_firstname_not_request(self): + class foo(object): + def __init__(self, context, request=None): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_newstyle_class_init_defaultargs_firstname_request(self): + class foo(object): + def __init__(self, request, foo=1, bar=2): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_newstyle_class_init_noargs(self): + class foo(object): + def __init__(): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_no_init(self): + class foo: + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_init_toomanyargs(self): + class foo: + def __init__(self, context, request): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_init_onearg_named_request(self): + class foo: + def __init__(self, request): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_oldstyle_class_init_onearg_named_somethingelse(self): + class foo: + def __init__(self, req): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_oldstyle_class_init_defaultargs_firstname_not_request(self): + class foo: + def __init__(self, context, request=None): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_oldstyle_class_init_defaultargs_firstname_request(self): + class foo: + def __init__(self, request, foo=1, bar=2): + """ """ + self.assertTrue(self._callFUT(foo), True) + + def test_oldstyle_class_init_noargs(self): + class foo: + def __init__(): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_function_toomanyargs(self): + def foo(context, request): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_function_onearg_named_request(self): + def foo(request): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_function_onearg_named_somethingelse(self): + def foo(req): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_function_defaultargs_firstname_not_request(self): + def foo(context, request=None): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_function_defaultargs_firstname_request(self): + def foo(request, foo=1, bar=2): + """ """ + self.assertTrue(self._callFUT(foo)) + + def test_function_noargs(self): + def foo(): + """ """ + self.assertFalse(self._callFUT(foo)) + + def test_instance_toomanyargs(self): + class Foo: + def __call__(self, context, request): + """ """ + foo = Foo() + self.assertFalse(self._callFUT(foo)) + + def test_instance_defaultargs_onearg_named_request(self): + class Foo: + def __call__(self, request): + """ """ + foo = Foo() + self.assertTrue(self._callFUT(foo)) + + def test_instance_defaultargs_onearg_named_somethingelse(self): + class Foo: + def __call__(self, req): + """ """ + foo = Foo() + self.assertTrue(self._callFUT(foo)) + + def test_instance_defaultargs_firstname_not_request(self): + class Foo: + def __call__(self, context, request=None): + """ """ + foo = Foo() + self.assertFalse(self._callFUT(foo)) + + def test_instance_defaultargs_firstname_request(self): + class Foo: + def __call__(self, request, foo=1, bar=2): + """ """ + foo = Foo() + self.assertTrue(self._callFUT(foo), True) + + def test_instance_nocall(self): + class Foo: pass + foo = Foo() + self.assertFalse(self._callFUT(foo)) + +class Test_isexception(unittest.TestCase): + def _callFUT(self, ob): + from pyramid.config import isexception + return isexception(ob) + + def test_is_exception_instance(self): + class E(Exception): + pass + e = E() + self.assertEqual(self._callFUT(e), True) + + def test_is_exception_class(self): + class E(Exception): + pass + self.assertEqual(self._callFUT(E), True) + + def test_is_IException(self): + from pyramid.interfaces import IException + self.assertEqual(self._callFUT(IException), True) + + def test_is_IException_subinterface(self): + from pyramid.interfaces import IException + class ISubException(IException): + pass + self.assertEqual(self._callFUT(ISubException), True) + +class TestActionPredicate(unittest.TestCase): + def _getTargetClass(self): + from pyramid.config import ActionPredicate + return ActionPredicate + + def _makeOne(self, action='myaction'): + return self._getTargetClass()(action) + + def test_bad_action_regex_string(self): + from pyramid.exceptions import ConfigurationError + cls = self._getTargetClass() + self.assertRaises(ConfigurationError, cls, '[a-z') + + def test_bad_action_regex_None(self): + from pyramid.exceptions import ConfigurationError + cls = self._getTargetClass() + self.assertRaises(ConfigurationError, cls, None) + + def test___call__no_matchdict(self): + pred = self._makeOne() + request = DummyRequest() + self.assertEqual(pred(None, request), False) + + def test___call__no_action_in_matchdict(self): + pred = self._makeOne() + request = DummyRequest() + request.matchdict = {} + self.assertEqual(pred(None, request), False) + + def test___call__action_does_not_match(self): + pred = self._makeOne() + request = DummyRequest() + request.matchdict = {'action':'notmyaction'} + self.assertEqual(pred(None, request), False) + + def test___call__action_matches(self): + pred = self._makeOne() + request = DummyRequest() + request.matchdict = {'action':'myaction'} + self.assertEqual(pred(None, request), True) + + def test___hash__(self): + pred1 = self._makeOne() + pred2 = self._makeOne() + pred3 = self._makeOne(action='notthesame') + self.assertEqual(hash(pred1), hash(pred2)) + self.assertNotEqual(hash(pred1), hash(pred3)) + self.assertNotEqual(hash(pred2), hash(pred3)) + + + +class DummyRequest: + subpath = () + matchdict = None + def __init__(self): + self.environ = {'PATH_INFO':'/static'} + self.params = {} + self.cookies = {} + def copy(self): + return self + def get_response(self, app): + return app + +class DummyContext: + pass + +class DummyLock: + def acquire(self): + self.acquired = True + + def release(self): + self.released = True + +class DummyPackage: + def __init__(self, name): + self.__name__ = name + +class DummyOverrides: + def __init__(self, package): + self.package = package + self.inserted = [] + + def insert(self, path, package, prefix): + self.inserted.append((path, package, prefix)) + +class DummyUnderOverride: + def __call__(self, package, path, override_package, override_prefix, + _info=u''): + self.package = package + self.path = path + self.override_package = override_package + self.override_prefix = override_prefix + +from zope.interface import Interface +class IDummy(Interface): + pass + +class IOther(Interface): + pass + +class DummyResponse: + status = '200 OK' + headerlist = () + app_iter = () + body = '' + +class DummyLogger: + def __init__(self): + self.messages = [] + def info(self, msg): + self.messages.append(msg) + warn = info + debug = info + +class DummySecurityPolicy: + def __init__(self, permitted=True): + self.permitted = permitted + + def effective_principals(self, request): + return [] + + def permits(self, context, principals, permission): + return self.permitted + +class DummyAccept(object): + def __init__(self, *matches): + self.matches = list(matches) + + def best_match(self, offered): + if self.matches: + for match in self.matches: + if match in offered: + self.matches.remove(match) + return match + def __contains__(self, val): + return val in self.matches + +from zope.interface import implements +from pyramid.interfaces import IMultiView +class DummyMultiView: + implements(IMultiView) + def __init__(self): + self.views = [] + self.name = 'name' + def add(self, view, order, accept=None, phash=None): + self.views.append((view, accept, phash)) + def __call__(self, context, request): + return 'OK1' + def __permitted__(self, context, request): + """ """ + +class DummyGetSiteManager(object): + def sethook(self, hook): + self.hook = hook + def reset(self): + self.unhooked = True + +class DummyThreadLocalManager(object): + pushed = None + popped = False + def push(self, d): + self.pushed = d + def pop(self): + self.popped = True + +class IFactory(Interface): + pass + +class DummyFactory(object): + implements(IFactory) + def __call__(self): + """ """ + +class DummyEvent: + implements(IDummy) + +class DummyStaticURLInfo: + def __init__(self): + self.added = [] + + def add(self, name, spec, **kw): + self.added.append((name, spec, kw)) + +def dummy_view(request): + return 'OK' + +def dummyfactory(request): + """ """ + +class DummyHandler(object): # pragma: no cover + def __init__(self, request): + self.request = request + + def action1(self): + return 'response 1' + + def action2(self): + return 'response 2' + +def dummy_include(config): + config.action('discrim', None, config.package) + diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py index 938db3c31..786c7539b 100644 --- a/pyramid/tests/test_configuration.py +++ b/pyramid/tests/test_configuration.py @@ -1,4537 +1,21 @@ import unittest -from pyramid import testing - -try: - import __pypy__ -except: - __pypy__ = None - class ConfiguratorTests(unittest.TestCase): + def setUp(self): + from zope.deprecation import __show__ + __show__.off() + + def tearDown(self): + from zope.deprecation import __show__ + __show__.on() + def _makeOne(self, *arg, **kw): from pyramid.configuration import Configurator return Configurator(*arg, **kw) - 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 _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 _getRouteRequestIface(self, config, name): - from pyramid.interfaces import IRouteRequest - iface = config.registry.getUtility(IRouteRequest, name) - return iface - - def _assertNotFound(self, wrapper, *arg): - from pyramid.exceptions import NotFound - self.assertRaises(NotFound, wrapper, *arg) - - def _registerEventListener(self, config, event_iface=None): - if event_iface is None: # pragma: no cover - from zope.interface import Interface - event_iface = Interface - L = [] - def subscriber(*event): - L.extend(event) - config.registry.registerHandler(subscriber, (event_iface,)) - return L - - def _registerLogger(self, config): - from pyramid.interfaces import IDebugLogger - logger = DummyLogger() - config.registry.registerUtility(logger, IDebugLogger) - return logger - - def _makeRequest(self, config): - request = DummyRequest() - request.registry = config.registry - return request - - def _registerSecurityPolicy(self, config, permissive): - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy - policy = DummySecurityPolicy(permissive) - config.registry.registerUtility(policy, IAuthenticationPolicy) - config.registry.registerUtility(policy, IAuthorizationPolicy) - - def _registerSettings(self, config, **settings): - config.registry.settings = settings - - def test_ctor_no_registry(self): - import sys - from pyramid.interfaces import ISettings - from pyramid.configuration import Configurator - from pyramid.interfaces import IRendererFactory - config = Configurator() - this_pkg = sys.modules['pyramid.tests'] - self.failUnless(config.registry.getUtility(ISettings)) - self.assertEqual(config.package, this_pkg) - self.failUnless(config.registry.getUtility(IRendererFactory, 'json')) - self.failUnless(config.registry.getUtility(IRendererFactory, 'string')) - if not __pypy__: - self.failUnless(config.registry.getUtility(IRendererFactory, '.pt')) - self.failUnless(config.registry.getUtility(IRendererFactory,'.txt')) - self.failUnless(config.registry.getUtility(IRendererFactory, '.mak')) - self.failUnless(config.registry.getUtility(IRendererFactory, '.mako')) - - def test_begin(self): - from pyramid.configuration import Configurator - config = Configurator() - manager = DummyThreadLocalManager() - config.manager = manager - config.begin() - self.assertEqual(manager.pushed, - {'registry':config.registry, 'request':None}) - self.assertEqual(manager.popped, False) - - def test_begin_with_request(self): - from pyramid.configuration import Configurator - config = Configurator() - request = object() - manager = DummyThreadLocalManager() - config.manager = manager - config.begin(request=request) - self.assertEqual(manager.pushed, - {'registry':config.registry, 'request':request}) - self.assertEqual(manager.popped, False) - - def test_end(self): - from pyramid.configuration import Configurator - config = Configurator() - manager = DummyThreadLocalManager() - config.manager = manager - config.end() - self.assertEqual(manager.pushed, None) - self.assertEqual(manager.popped, True) - - def test_ctor_with_package_registry(self): - import sys - from pyramid.configuration import Configurator - pkg = sys.modules['pyramid'] - config = Configurator(package=pkg) - self.assertEqual(config.package, pkg) - - def test_ctor_noreg_custom_settings(self): - from pyramid.interfaces import ISettings - settings = {'reload_templates':True, - 'mysetting':True} - config = self._makeOne(settings=settings) - settings = config.registry.getUtility(ISettings) - self.assertEqual(settings['reload_templates'], True) - self.assertEqual(settings['debug_authorization'], False) - self.assertEqual(settings['mysetting'], True) - - def test_ctor_noreg_debug_logger_None_default(self): - from pyramid.interfaces import IDebugLogger - config = self._makeOne() - logger = config.registry.getUtility(IDebugLogger) - self.assertEqual(logger.name, 'pyramid.debug') - - def test_ctor_noreg_debug_logger_non_None(self): - from pyramid.interfaces import IDebugLogger - logger = object() - config = self._makeOne(debug_logger=logger) - result = config.registry.getUtility(IDebugLogger) - self.assertEqual(logger, result) - - def test_ctor_authentication_policy(self): - from pyramid.interfaces import IAuthenticationPolicy - policy = object() - config = self._makeOne(authentication_policy=policy) - result = config.registry.getUtility(IAuthenticationPolicy) - self.assertEqual(policy, result) - - def test_ctor_authorization_policy_only(self): - from pyramid.exceptions import ConfigurationError - policy = object() - self.assertRaises(ConfigurationError, - self._makeOne, authorization_policy=policy) - - def test_ctor_no_root_factory(self): - from pyramid.interfaces import IRootFactory - config = self._makeOne() - self.failUnless(config.registry.getUtility(IRootFactory)) - - def test_ctor_alternate_renderers(self): - from pyramid.interfaces import IRendererFactory - renderer = object() - config = self._makeOne(renderers=[('yeah', renderer)]) - self.assertEqual(config.registry.getUtility(IRendererFactory, 'yeah'), - renderer) - - def test_ctor_default_permission(self): - from pyramid.interfaces import IDefaultPermission - config = self._makeOne(default_permission='view') - self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view') - - def test_ctor_session_factory(self): - from pyramid.interfaces import ISessionFactory - config = self._makeOne(session_factory='factory') - self.assertEqual(config.registry.getUtility(ISessionFactory), 'factory') - - def test_with_package_module(self): - from pyramid.tests import test_configuration - import pyramid.tests - config = self._makeOne() - newconfig = config.with_package(test_configuration) - self.assertEqual(newconfig.package, pyramid.tests) - - def test_with_package_package(self): - import pyramid.tests - config = self._makeOne() - newconfig = config.with_package(pyramid.tests) - self.assertEqual(newconfig.package, pyramid.tests) - - def test_maybe_dotted_string_success(self): - import pyramid.tests - config = self._makeOne() - result = config.maybe_dotted('pyramid.tests') - self.assertEqual(result, pyramid.tests) - - def test_maybe_dotted_string_fail(self): - config = self._makeOne() - self.assertRaises(ImportError, - config.maybe_dotted, 'cant.be.found') - - def test_maybe_dotted_notstring_success(self): - import pyramid.tests - config = self._makeOne() - result = config.maybe_dotted(pyramid.tests) - self.assertEqual(result, pyramid.tests) - - def test_absolute_resource_spec_already_absolute(self): - import pyramid.tests - config = self._makeOne(package=pyramid.tests) - result = config.absolute_resource_spec('already:absolute') - self.assertEqual(result, 'already:absolute') - - def test_absolute_resource_spec_notastring(self): - import pyramid.tests - config = self._makeOne(package=pyramid.tests) - result = config.absolute_resource_spec(None) - self.assertEqual(result, None) - - def test_absolute_resource_spec_relative(self): - import pyramid.tests - config = self._makeOne(package=pyramid.tests) - result = config.absolute_resource_spec('templates') - self.assertEqual(result, 'pyramid.tests:templates') - - def test_setup_registry_fixed(self): - class DummyRegistry(object): - def subscribers(self, events, name): - self.events = events - return events - def registerUtility(self, *arg, **kw): - pass - reg = DummyRegistry() - config = self._makeOne(reg) - config.add_view = lambda *arg, **kw: False - config.setup_registry() - self.assertEqual(reg.has_listeners, True) - self.assertEqual(reg.notify(1), None) - self.assertEqual(reg.events, (1,)) - - def test_setup_registry_registers_default_exceptionresponse_view(self): - from pyramid.interfaces import IExceptionResponse - from pyramid.view import default_exceptionresponse_view - class DummyRegistry(object): - def registerUtility(self, *arg, **kw): - pass - reg = DummyRegistry() - config = self._makeOne(reg) - views = [] - config.add_view = lambda *arg, **kw: views.append((arg, kw)) - config.setup_registry() - self.assertEqual(views[0], ((default_exceptionresponse_view,), - {'context':IExceptionResponse})) - - def test_setup_registry_explicit_notfound_trumps_iexceptionresponse(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.exceptions import NotFound - from pyramid.registry import Registry - reg = Registry() - config = self._makeOne(reg) - config.setup_registry() # registers IExceptionResponse default view - def myview(context, request): - return 'OK' - config.add_view(myview, context=NotFound) - request = self._makeRequest(config) - view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, 'OK') - - def test_setup_registry_custom_settings(self): - from pyramid.registry import Registry - from pyramid.interfaces import ISettings - settings = {'reload_templates':True, - 'mysetting':True} - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(settings=settings) - settings = reg.getUtility(ISettings) - self.assertEqual(settings['reload_templates'], True) - self.assertEqual(settings['debug_authorization'], False) - self.assertEqual(settings['mysetting'], True) - - def test_setup_registry_debug_logger_None_default(self): - from pyramid.registry import Registry - from pyramid.interfaces import IDebugLogger - reg = Registry() - config = self._makeOne(reg) - config.setup_registry() - logger = reg.getUtility(IDebugLogger) - self.assertEqual(logger.name, 'pyramid.debug') - - def test_setup_registry_debug_logger_non_None(self): - from pyramid.registry import Registry - from pyramid.interfaces import IDebugLogger - logger = object() - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(debug_logger=logger) - result = reg.getUtility(IDebugLogger) - self.assertEqual(logger, result) - - def test_setup_registry_debug_logger_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import IDebugLogger - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(debug_logger='pyramid.tests') - result = reg.getUtility(IDebugLogger) - import pyramid.tests - self.assertEqual(result, pyramid.tests) - - def test_setup_registry_authentication_policy(self): - from pyramid.registry import Registry - from pyramid.interfaces import IAuthenticationPolicy - policy = object() - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(authentication_policy=policy) - result = reg.getUtility(IAuthenticationPolicy) - self.assertEqual(policy, result) - - def test_setup_registry_authentication_policy_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import IAuthenticationPolicy - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(authentication_policy='pyramid.tests') - result = reg.getUtility(IAuthenticationPolicy) - import pyramid.tests - self.assertEqual(result, pyramid.tests) - - def test_setup_registry_authorization_policy_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import IAuthorizationPolicy - reg = Registry() - config = self._makeOne(reg) - dummy = object() - config.setup_registry(authentication_policy=dummy, - authorization_policy='pyramid.tests') - result = reg.getUtility(IAuthorizationPolicy) - import pyramid.tests - self.assertEqual(result, pyramid.tests) - - def test_setup_registry_authorization_policy_only(self): - from pyramid.registry import Registry - from pyramid.exceptions import ConfigurationError - policy = object() - reg = Registry() - config = self._makeOne(reg) - config = self.assertRaises(ConfigurationError, - config.setup_registry, - authorization_policy=policy) - - def test_setup_registry_default_root_factory(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRootFactory - reg = Registry() - config = self._makeOne(reg) - config.setup_registry() - self.failUnless(reg.getUtility(IRootFactory)) - - def test_setup_registry_dottedname_root_factory(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRootFactory - reg = Registry() - config = self._makeOne(reg) - import pyramid.tests - config.setup_registry(root_factory='pyramid.tests') - self.assertEqual(reg.getUtility(IRootFactory), pyramid.tests) - - def test_setup_registry_locale_negotiator_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import ILocaleNegotiator - reg = Registry() - config = self._makeOne(reg) - import pyramid.tests - config.setup_registry(locale_negotiator='pyramid.tests') - utility = reg.getUtility(ILocaleNegotiator) - self.assertEqual(utility, pyramid.tests) - - def test_setup_registry_locale_negotiator(self): - from pyramid.registry import Registry - from pyramid.interfaces import ILocaleNegotiator - reg = Registry() - config = self._makeOne(reg) - negotiator = object() - config.setup_registry(locale_negotiator=negotiator) - utility = reg.getUtility(ILocaleNegotiator) - self.assertEqual(utility, negotiator) - - def test_setup_registry_request_factory(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRequestFactory - reg = Registry() - config = self._makeOne(reg) - factory = object() - config.setup_registry(request_factory=factory) - utility = reg.getUtility(IRequestFactory) - self.assertEqual(utility, factory) - - def test_setup_registry_request_factory_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRequestFactory - reg = Registry() - config = self._makeOne(reg) - import pyramid.tests - config.setup_registry(request_factory='pyramid.tests') - utility = reg.getUtility(IRequestFactory) - self.assertEqual(utility, pyramid.tests) - - def test_setup_registry_renderer_globals_factory(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRendererGlobalsFactory - reg = Registry() - config = self._makeOne(reg) - factory = object() - config.setup_registry(renderer_globals_factory=factory) - utility = reg.getUtility(IRendererGlobalsFactory) - self.assertEqual(utility, factory) - - def test_setup_registry_renderer_globals_factory_dottedname(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRendererGlobalsFactory - reg = Registry() - config = self._makeOne(reg) - import pyramid.tests - config.setup_registry(renderer_globals_factory='pyramid.tests') - utility = reg.getUtility(IRendererGlobalsFactory) - self.assertEqual(utility, pyramid.tests) - - def test_setup_registry_alternate_renderers(self): - from pyramid.registry import Registry - from pyramid.interfaces import IRendererFactory - renderer = object() - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(renderers=[('yeah', renderer)]) - self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'), - renderer) - - def test_setup_registry_default_permission(self): - from pyramid.registry import Registry - from pyramid.interfaces import IDefaultPermission - reg = Registry() - config = self._makeOne(reg) - config.setup_registry(default_permission='view') - self.assertEqual(reg.getUtility(IDefaultPermission), 'view') - - def test_get_settings_nosettings(self): - from pyramid.registry import Registry - reg = Registry() - config = self._makeOne(reg) - self.assertEqual(config.get_settings(), None) - - def test_get_settings_withsettings(self): - settings = {'a':1} - config = self._makeOne() - config.registry.settings = settings - self.assertEqual(config.get_settings(), settings) - - def test_add_settings_settings_already_registered(self): - from pyramid.registry import Registry - reg = Registry() - config = self._makeOne(reg) - config._set_settings({'a':1}) - config.add_settings({'b':2}) - settings = reg.settings - self.assertEqual(settings['a'], 1) - self.assertEqual(settings['b'], 2) - - def test_add_settings_settings_not_yet_registered(self): - from pyramid.registry import Registry - from pyramid.interfaces import ISettings - reg = Registry() - config = self._makeOne(reg) - config.add_settings({'a':1}) - settings = reg.getUtility(ISettings) - self.assertEqual(settings['a'], 1) - - def test_add_subscriber_defaults(self): - from zope.interface import implements - from zope.interface import Interface - class IEvent(Interface): - pass - class Event: - implements(IEvent) - L = [] - def subscriber(event): - L.append(event) - config = self._makeOne() - config.add_subscriber(subscriber) - event = Event() - config.registry.notify(event) - self.assertEqual(len(L), 1) - self.assertEqual(L[0], event) - config.registry.notify(object()) - self.assertEqual(len(L), 2) - - def test_add_subscriber_iface_specified(self): - from zope.interface import implements - from zope.interface import Interface - class IEvent(Interface): - pass - class Event: - implements(IEvent) - L = [] - def subscriber(event): - L.append(event) - config = self._makeOne() - config.add_subscriber(subscriber, IEvent) - event = Event() - config.registry.notify(event) - self.assertEqual(len(L), 1) - self.assertEqual(L[0], event) - config.registry.notify(object()) - self.assertEqual(len(L), 1) - - def test_add_subscriber_dottednames(self): - import pyramid.tests - from pyramid.interfaces import INewRequest - config = self._makeOne() - config.add_subscriber('pyramid.tests', - 'pyramid.interfaces.INewRequest') - handlers = list(config.registry.registeredHandlers()) - self.assertEqual(len(handlers), 1) - handler = handlers[0] - self.assertEqual(handler.handler, pyramid.tests) - self.assertEqual(handler.required, (INewRequest,)) - - def test_add_object_event_subscriber(self): - from zope.interface import implements - from zope.interface import Interface - class IEvent(Interface): - pass - class Event: - object = 'foo' - implements(IEvent) - event = Event() - L = [] - def subscriber(object, event): - L.append(event) - config = self._makeOne() - config.add_subscriber(subscriber, (Interface, IEvent)) - config.registry.subscribers((event.object, event), None) - self.assertEqual(len(L), 1) - self.assertEqual(L[0], event) - config.registry.subscribers((event.object, IDummy), None) - self.assertEqual(len(L), 1) - - def test_make_wsgi_app(self): - from pyramid.router import Router - from pyramid.interfaces import IApplicationCreated - manager = DummyThreadLocalManager() - config = self._makeOne() - subscriber = self._registerEventListener(config, IApplicationCreated) - config.manager = manager - app = config.make_wsgi_app() - self.assertEqual(app.__class__, Router) - self.assertEqual(manager.pushed['registry'], config.registry) - self.assertEqual(manager.pushed['request'], None) - self.failUnless(manager.popped) - self.assertEqual(len(subscriber), 1) - self.failUnless(IApplicationCreated.providedBy(subscriber[0])) - - def test_load_zcml_default(self): - import pyramid.tests.fixtureapp - config = self._makeOne(package=pyramid.tests.fixtureapp) - registry = config.load_zcml() - from pyramid.tests.fixtureapp.models import IFixture - self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_routesapp(self): - from pyramid.interfaces import IRoutesMapper - config = self._makeOne() - config.load_zcml('pyramid.tests.routesapp:configure.zcml') - self.failUnless(config.registry.getUtility(IRoutesMapper)) - - def test_load_zcml_fixtureapp(self): - from pyramid.tests.fixtureapp.models import IFixture - config = self._makeOne() - config.load_zcml('pyramid.tests.fixtureapp:configure.zcml') - self.failUnless(config.registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_as_relative_filename(self): - import pyramid.tests.fixtureapp - config = self._makeOne(package=pyramid.tests.fixtureapp) - registry = config.load_zcml('configure.zcml') - from pyramid.tests.fixtureapp.models import IFixture - self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_as_absolute_filename(self): - import os - import pyramid.tests.fixtureapp - config = self._makeOne(package=pyramid.tests.fixtureapp) - dn = os.path.dirname(pyramid.tests.fixtureapp.__file__) - c_z = os.path.join(dn, 'configure.zcml') - registry = config.load_zcml(c_z) - from pyramid.tests.fixtureapp.models import IFixture - self.failUnless(registry.queryUtility(IFixture)) # only in c.zcml - - def test_load_zcml_lock_and_unlock(self): - config = self._makeOne() - dummylock = DummyLock() - config.load_zcml( - 'pyramid.tests.fixtureapp:configure.zcml', - lock=dummylock) - self.assertEqual(dummylock.acquired, True) - self.assertEqual(dummylock.released, True) - - def test_add_view_view_callable_None_no_renderer(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.add_view) - - def test_add_view_with_request_type_and_route_name(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - view = lambda *arg: 'OK' - self.assertRaises(ConfigurationError, config.add_view, view, '', None, - None, True, True) - - def test_add_view_with_request_type(self): - from zope.interface import directlyProvides - from pyramid.interfaces import IRequest - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, - request_type='pyramid.interfaces.IRequest') - wrapper = self._getViewCallable(config) - request = DummyRequest() - self._assertNotFound(wrapper, None, request) - directlyProvides(request, IRequest) - result = wrapper(None, request) - self.assertEqual(result, 'OK') - - def test_add_view_view_callable_None_with_renderer(self): - config = self._makeOne() - self._registerRenderer(config, name='dummy') - config.add_view(renderer='dummy') - view = self._getViewCallable(config) - self.failUnless('Hello!' in view(None, None).body) - - def test_add_view_wrapped_view_is_decorated(self): - def view(request): # request-only wrapper - """ """ - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - self.assertEqual(wrapper.__module__, view.__module__) - self.assertEqual(wrapper.__name__, view.__name__) - self.assertEqual(wrapper.__doc__, view.__doc__) - - def test_add_view_view_callable_dottedname(self): - config = self._makeOne() - config.add_view(view='pyramid.tests.test_configuration.dummy_view') - wrapper = self._getViewCallable(config) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_with_function_callable(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_with_function_callable_requestonly(self): - def view(request): - return 'OK' - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_instance(self): - class AView: - def __call__(self, context, request): - """ """ - return 'OK' - view = AView() - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_instance_requestonly(self): - class AView: - def __call__(self, request): - """ """ - return 'OK' - view = AView() - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_oldstyle_class(self): - class view: - def __init__(self, context, request): - self.context = context - self.request = request - - def __call__(self): - return 'OK' - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_as_oldstyle_class_requestonly(self): - class view: - def __init__(self, request): - self.request = request - - def __call__(self): - return 'OK' - config = self._makeOne() - config.add_view(view=view) - wrapper = self._getViewCallable(config) - result = wrapper(None, None) - self.assertEqual(result, 'OK') - - def test_add_view_context_as_class(self): - from zope.interface import implementedBy - view = lambda *arg: 'OK' - class Foo: - pass - config = self._makeOne() - config.add_view(context=Foo, view=view) - foo = implementedBy(Foo) - wrapper = self._getViewCallable(config, foo) - self.assertEqual(wrapper, view) - - def test_add_view_context_as_iface(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(context=IDummy, view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_context_as_dottedname(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(context='pyramid.tests.test_configuration.IDummy', - view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_for__as_dottedname(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(for_='pyramid.tests.test_configuration.IDummy', - view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_for_as_class(self): - # ``for_`` is older spelling for ``context`` - from zope.interface import implementedBy - view = lambda *arg: 'OK' - class Foo: - pass - config = self._makeOne() - config.add_view(for_=Foo, view=view) - foo = implementedBy(Foo) - wrapper = self._getViewCallable(config, foo) - self.assertEqual(wrapper, view) - - def test_add_view_for_as_iface(self): - # ``for_`` is older spelling for ``context`` - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(for_=IDummy, view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_context_trumps_for(self): - # ``for_`` is older spelling for ``context`` - view = lambda *arg: 'OK' - config = self._makeOne() - class Foo: - pass - config.add_view(context=IDummy, for_=Foo, view=view) - wrapper = self._getViewCallable(config, IDummy) - self.assertEqual(wrapper, view) - - def test_add_view_register_secured_view(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import ISecuredView - from pyramid.interfaces import IViewClassifier - view = lambda *arg: 'OK' - view.__call_permissive__ = view - config = self._makeOne() - config.add_view(view=view) - wrapper = config.registry.adapters.lookup( - (IViewClassifier, IRequest, Interface), - ISecuredView, name='', default=None) - self.assertEqual(wrapper, view) - - def test_add_view_exception_register_secured_view(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IExceptionViewClassifier - view = lambda *arg: 'OK' - view.__call_permissive__ = view - config = self._makeOne() - config.add_view(view=view, context=RuntimeError) - wrapper = config.registry.adapters.lookup( - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='', default=None) - self.assertEqual(wrapper, view) - - def test_add_view_same_phash_overrides_existing_single_view(self): - from pyramid.compat import md5 - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - phash = md5() - phash.update('xhr:True') - view = lambda *arg: 'NOT OK' - view.__phash__ = phash.hexdigest() - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, xhr=True) - wrapper = self._getViewCallable(config) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_exc_same_phash_overrides_existing_single_view(self): - from pyramid.compat import md5 - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IExceptionViewClassifier - from pyramid.interfaces import IMultiView - phash = md5() - phash.update('xhr:True') - view = lambda *arg: 'NOT OK' - view.__phash__ = phash.hexdigest() - config = self._makeOne() - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, xhr=True, - context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_default_phash_overrides_no_phash(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview) - wrapper = self._getViewCallable(config) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_exc_default_phash_overrides_no_phash(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IExceptionViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - config = self._makeOne() - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_default_phash_overrides_default_phash(self): - from pyramid.configuration import DEFAULT_PHASH - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - view.__phash__ = DEFAULT_PHASH - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview) - wrapper = self._getViewCallable(config) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_exc_default_phash_overrides_default_phash(self): - from pyramid.configuration import DEFAULT_PHASH - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IExceptionViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'NOT OK' - view.__phash__ = DEFAULT_PHASH - config = self._makeOne() - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - def newview(context, request): - return 'OK' - config.add_view(view=newview, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failIf(IMultiView.providedBy(wrapper)) - request = DummyRequest() - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_multiview_replaces_existing_view(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - config.add_view(view=view) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_exc_multiview_replaces_existing_view(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IExceptionViewClassifier - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IMultiView - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne() - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.add_view(view=view, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_multiview_replaces_existing_securedview(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import ISecuredView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), - ISecuredView, name='') - config.add_view(view=view) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_exc_multiview_replaces_existing_securedview(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import ISecuredView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - view = lambda *arg: 'OK' - view.__phash__ = 'abc' - config = self._makeOne() - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - ISecuredView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - ISecuredView, name='') - config.add_view(view=view, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_view_with_accept_multiview_replaces_existing_view(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - def view(context, request): - return 'OK' - def view2(context, request): - return 'OK2' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - config.add_view(view=view2, accept='text/html') - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(len(wrapper.views), 1) - self.assertEqual(len(wrapper.media_views), 1) - self.assertEqual(wrapper(None, None), 'OK') - request = DummyRequest() - request.accept = DummyAccept('text/html', 'text/html') - self.assertEqual(wrapper(None, request), 'OK2') - - def test_add_view_exc_with_accept_multiview_replaces_existing_view(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - def view(context, request): - return 'OK' - def view2(context, request): - return 'OK2' - config = self._makeOne() - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.add_view(view=view2, accept='text/html', context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(len(wrapper.views), 1) - self.assertEqual(len(wrapper.media_views), 1) - self.assertEqual(wrapper(None, None), 'OK') - request = DummyRequest() - request.accept = DummyAccept('text/html', 'text/html') - self.assertEqual(wrapper(None, request), 'OK2') - - def test_add_view_multiview_replaces_existing_view_with___accept__(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - def view(context, request): - return 'OK' - def view2(context, request): - return 'OK2' - view.__accept__ = 'text/html' - view.__phash__ = 'abc' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), IView, name='') - config.add_view(view=view2) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(len(wrapper.views), 1) - self.assertEqual(len(wrapper.media_views), 1) - self.assertEqual(wrapper(None, None), 'OK2') - request = DummyRequest() - request.accept = DummyAccept('text/html') - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_exc_mulview_replaces_existing_view_with___accept__(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - def view(context, request): - return 'OK' - def view2(context, request): - return 'OK2' - view.__accept__ = 'text/html' - view.__phash__ = 'abc' - config = self._makeOne() - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IView, name='') - config.add_view(view=view2, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual(len(wrapper.views), 1) - self.assertEqual(len(wrapper.media_views), 1) - self.assertEqual(wrapper(None, None), 'OK2') - request = DummyRequest() - request.accept = DummyAccept('text/html') - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_multiview_replaces_multiview(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - view = DummyMultiView() - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Interface), - IMultiView, name='') - view2 = lambda *arg: 'OK2' - config.add_view(view=view2) - wrapper = self._getViewCallable(config) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) - self.assertEqual(wrapper(None, None), 'OK1') - - def test_add_view_exc_multiview_replaces_multiview(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - view = DummyMultiView() - config = self._makeOne() - config.registry.registerAdapter( - view, - (IViewClassifier, IRequest, implementedBy(RuntimeError)), - IMultiView, name='') - config.registry.registerAdapter( - view, - (IExceptionViewClassifier, IRequest, implementedBy(RuntimeError)), - IMultiView, name='') - view2 = lambda *arg: 'OK2' - config.add_view(view=view2, context=RuntimeError) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), exception_view=True) - self.failUnless(IMultiView.providedBy(wrapper)) - self.assertEqual([x[:2] for x in wrapper.views], [(view2, None)]) - self.assertEqual(wrapper(None, None), 'OK1') - - def test_add_view_multiview_context_superclass_then_subclass(self): - from zope.interface import Interface - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - class ISuper(Interface): - pass - class ISub(ISuper): - pass - view = lambda *arg: 'OK' - view2 = lambda *arg: 'OK2' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, ISuper), IView, name='') - config.add_view(view=view2, for_=ISub) - wrapper = self._getViewCallable(config, ISuper, IRequest) - self.failIf(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK') - wrapper = self._getViewCallable(config, ISub, IRequest) - self.failIf(IMultiView.providedBy(wrapper)) - self.assertEqual(wrapper(None, None), 'OK2') - - def test_add_view_multiview_exception_superclass_then_subclass(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.interfaces import IView - from pyramid.interfaces import IMultiView - from pyramid.interfaces import IViewClassifier - from pyramid.interfaces import IExceptionViewClassifier - class Super(Exception): - pass - class Sub(Super): - pass - view = lambda *arg: 'OK' - view2 = lambda *arg: 'OK2' - config = self._makeOne() - config.registry.registerAdapter( - view, (IViewClassifier, IRequest, Super), IView, name='') - config.registry.registerAdapter( - view, (IExceptionViewClassifier, IRequest, Super), IView, name='') - config.add_view(view=view2, for_=Sub) - wrapper = self._getViewCallable( - config, implementedBy(Super), IRequest) - wrapper_exc_view = self._getViewCallable( - config, implementedBy(Super), IRequest, exception_view=True) - self.assertEqual(wrapper_exc_view, wrapper) - self.failIf(IMultiView.providedBy(wrapper_exc_view)) - self.assertEqual(wrapper_exc_view(None, None), 'OK') - wrapper = self._getViewCallable( - config, implementedBy(Sub), IRequest) - wrapper_exc_view = self._getViewCallable( - config, implementedBy(Sub), IRequest, exception_view=True) - self.assertEqual(wrapper_exc_view, wrapper) - self.failIf(IMultiView.providedBy(wrapper_exc_view)) - self.assertEqual(wrapper_exc_view(None, None), 'OK2') - - def test_add_view_multiview_call_ordering(self): - from zope.interface import directlyProvides - def view1(context, request): return 'view1' - def view2(context, request): return 'view2' - def view3(context, request): return 'view3' - def view4(context, request): return 'view4' - def view5(context, request): return 'view5' - def view6(context, request): return 'view6' - def view7(context, request): return 'view7' - def view8(context, request): return 'view8' - config = self._makeOne() - config.add_view(view=view1) - config.add_view(view=view2, request_method='POST') - config.add_view(view=view3,request_param='param') - config.add_view(view=view4, containment=IDummy) - config.add_view(view=view5, request_method='POST',request_param='param') - config.add_view(view=view6, request_method='POST', containment=IDummy) - config.add_view(view=view7, request_param='param', containment=IDummy) - config.add_view(view=view8, request_method='POST',request_param='param', - containment=IDummy) - - wrapper = self._getViewCallable(config) - - ctx = DummyContext() - request = self._makeRequest(config) - request.method = 'GET' - request.params = {} - self.assertEqual(wrapper(ctx, request), 'view1') - - ctx = DummyContext() - request = self._makeRequest(config) - request.params = {} - request.method = 'POST' - self.assertEqual(wrapper(ctx, request), 'view2') - - ctx = DummyContext() - request = self._makeRequest(config) - request.params = {'param':'1'} - request.method = 'GET' - self.assertEqual(wrapper(ctx, request), 'view3') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.method = 'GET' - request.params = {} - self.assertEqual(wrapper(ctx, request), 'view4') - - ctx = DummyContext() - request = self._makeRequest(config) - request.method = 'POST' - request.params = {'param':'1'} - self.assertEqual(wrapper(ctx, request), 'view5') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.params = {} - request.method = 'POST' - self.assertEqual(wrapper(ctx, request), 'view6') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.method = 'GET' - request.params = {'param':'1'} - self.assertEqual(wrapper(ctx, request), 'view7') - - ctx = DummyContext() - directlyProvides(ctx, IDummy) - request = self._makeRequest(config) - request.method = 'POST' - request.params = {'param':'1'} - self.assertEqual(wrapper(ctx, request), 'view8') - - def test_add_view_with_template_renderer(self): - import pyramid.tests - from pyramid.interfaces import ISettings - class view(object): - def __init__(self, context, request): - self.request = request - self.context = context - - def __call__(self): - return {'a':'1'} - config = self._makeOne() - renderer = self._registerRenderer(config) - fixture = 'pyramid.tests:fixtures/minimal.txt' - config.add_view(view=view, renderer=fixture) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - result = wrapper(None, request) - self.assertEqual(result.body, 'Hello!') - settings = config.registry.queryUtility(ISettings) - result = renderer.info - self.assertEqual(result.registry, config.registry) - self.assertEqual(result.type, '.txt') - self.assertEqual(result.package, pyramid.tests) - self.assertEqual(result.name, fixture) - self.assertEqual(result.settings, settings) - - def test_add_view_with_template_renderer_no_callable(self): - import pyramid.tests - from pyramid.interfaces import ISettings - config = self._makeOne() - renderer = self._registerRenderer(config) - fixture = 'pyramid.tests:fixtures/minimal.txt' - config.add_view(view=None, renderer=fixture) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - result = wrapper(None, request) - self.assertEqual(result.body, 'Hello!') - settings = config.registry.queryUtility(ISettings) - result = renderer.info - self.assertEqual(result.registry, config.registry) - self.assertEqual(result.type, '.txt') - self.assertEqual(result.package, pyramid.tests) - self.assertEqual(result.name, fixture) - self.assertEqual(result.settings, settings) - - def test_add_view_with_request_type_as_iface(self): - from zope.interface import directlyProvides - def view(context, request): - return 'OK' - config = self._makeOne() - config.add_view(request_type=IDummy, view=view) - wrapper = self._getViewCallable(config, None) - request = self._makeRequest(config) - directlyProvides(request, IDummy) - result = wrapper(None, request) - self.assertEqual(result, 'OK') - - def test_add_view_with_request_type_as_noniface(self): - from pyramid.exceptions import ConfigurationError - view = lambda *arg: 'OK' - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_view, view, '', None, None, object) - - def test_add_view_with_route_name(self): - from zope.component import ComponentLookupError - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, route_name='foo') - self.assertEqual(len(config.registry.deferred_route_views), 1) - infos = config.registry.deferred_route_views['foo'] - self.assertEqual(len(infos), 1) - info = infos[0] - self.assertEqual(info['route_name'], 'foo') - self.assertEqual(info['view'], view) - self.assertRaises(ComponentLookupError, - self._getRouteRequestIface, config, 'foo') - wrapper = self._getViewCallable(config, None) - self.assertEqual(wrapper, None) - config.add_route('foo', '/a/b') - request_iface = self._getRouteRequestIface(config, 'foo') - self.failIfEqual(request_iface, None) - wrapper = self._getViewCallable(config, request_iface=request_iface) - self.failIfEqual(wrapper, None) - self.assertEqual(wrapper(None, None), 'OK') - - def test_deferred_route_views_retains_custom_predicates(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, route_name='foo', custom_predicates=('123',)) - self.assertEqual(len(config.registry.deferred_route_views), 1) - infos = config.registry.deferred_route_views['foo'] - self.assertEqual(len(infos), 1) - info = infos[0] - self.assertEqual(info['route_name'], 'foo') - self.assertEqual(info['custom_predicates'], ('123',)) - - def test_add_view_with_route_name_exception(self): - from zope.interface import implementedBy - from zope.component import ComponentLookupError - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, route_name='foo', context=RuntimeError) - self.assertEqual(len(config.registry.deferred_route_views), 1) - infos = config.registry.deferred_route_views['foo'] - self.assertEqual(len(infos), 1) - info = infos[0] - self.assertEqual(info['route_name'], 'foo') - self.assertEqual(info['view'], view) - self.assertRaises(ComponentLookupError, - self._getRouteRequestIface, config, 'foo') - wrapper_exc_view = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - exception_view=True) - self.assertEqual(wrapper_exc_view, None) - config.add_route('foo', '/a/b') - request_iface = self._getRouteRequestIface(config, 'foo') - self.failIfEqual(request_iface, None) - wrapper_exc_view = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - request_iface=request_iface, exception_view=True) - self.failIfEqual(wrapper_exc_view, None) - wrapper = self._getViewCallable( - config, ctx_iface=implementedBy(RuntimeError), - request_iface=request_iface) - self.assertEqual(wrapper_exc_view, wrapper) - self.assertEqual(wrapper_exc_view(None, None), 'OK') - - def test_add_view_with_request_method_true(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, request_method='POST') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.method = 'POST' - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_request_method_false(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, request_method='POST') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.method = 'GET' - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_request_param_noval_true(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, request_param='abc') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.params = {'abc':''} - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_request_param_noval_false(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, request_param='abc') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.params = {} - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_request_param_val_true(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, request_param='abc=123') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.params = {'abc':'123'} - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_request_param_val_false(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, request_param='abc=123') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.params = {'abc':''} - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_xhr_true(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, xhr=True) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_xhr_false(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, xhr=True) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.is_xhr = False - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_header_badregex(self): - from pyramid.exceptions import ConfigurationError - view = lambda *arg: 'OK' - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_view, view=view, header='Host:a\\') - - def test_add_view_with_header_noval_match(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, header='Host') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'Host':'whatever'} - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_header_noval_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, header='Host') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'NotHost':'whatever'} - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_header_val_match(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, header=r'Host:\d') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'Host':'1'} - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_header_val_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, header=r'Host:\d') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'Host':'abc'} - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_header_val_missing(self): - from pyramid.exceptions import NotFound - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, header=r'Host:\d') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.headers = {'NoHost':'1'} - self.assertRaises(NotFound, wrapper, None, request) - - def test_add_view_with_accept_match(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, accept='text/xml') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.accept = ['text/xml'] - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_accept_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, accept='text/xml') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.accept = ['text/html'] - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_containment_true(self): - from zope.interface import directlyProvides - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, containment=IDummy) - wrapper = self._getViewCallable(config) - context = DummyContext() - directlyProvides(context, IDummy) - self.assertEqual(wrapper(context, None), 'OK') - - def test_add_view_with_containment_false(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, containment=IDummy) - wrapper = self._getViewCallable(config) - context = DummyContext() - self._assertNotFound(wrapper, context, None) - - def test_add_view_with_containment_dottedname(self): - from zope.interface import directlyProvides - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view( - view=view, - containment='pyramid.tests.test_configuration.IDummy') - wrapper = self._getViewCallable(config) - context = DummyContext() - directlyProvides(context, IDummy) - self.assertEqual(wrapper(context, None), 'OK') - - def test_add_view_with_path_info_badregex(self): - from pyramid.exceptions import ConfigurationError - view = lambda *arg: 'OK' - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_view, view=view, path_info='\\') - - def test_add_view_with_path_info_match(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, path_info='/foo') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.path_info = '/foo' - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_path_info_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne() - config.add_view(view=view, path_info='/foo') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.path_info = '/' - self._assertNotFound(wrapper, None, request) - - def test_add_view_with_custom_predicates_match(self): - view = lambda *arg: 'OK' - config = self._makeOne() - def pred1(context, request): - return True - def pred2(context, request): - return True - predicates = (pred1, pred2) - config.add_view(view=view, custom_predicates=predicates) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_with_custom_predicates_nomatch(self): - view = lambda *arg: 'OK' - config = self._makeOne() - def pred1(context, request): - return True - def pred2(context, request): - return False - predicates = (pred1, pred2) - config.add_view(view=view, custom_predicates=predicates) - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - self._assertNotFound(wrapper, None, request) - - def test_add_view_custom_predicate_bests_standard_predicate(self): - view = lambda *arg: 'OK' - view2 = lambda *arg: 'NOT OK' - config = self._makeOne() - def pred1(context, request): - return True - config.add_view(view=view, custom_predicates=(pred1,)) - config.add_view(view=view2, request_method='GET') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.method = 'GET' - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_custom_more_preds_first_bests_fewer_preds_last(self): - view = lambda *arg: 'OK' - view2 = lambda *arg: 'NOT OK' - config = self._makeOne() - config.add_view(view=view, request_method='GET', xhr=True) - config.add_view(view=view2, request_method='GET') - wrapper = self._getViewCallable(config) - request = self._makeRequest(config) - request.method = 'GET' - request.is_xhr = True - self.assertEqual(wrapper(None, request), 'OK') - - def test_add_view_same_predicates(self): - view2 = lambda *arg: 'second' - view1 = lambda *arg: 'first' - config = self._makeOne() - config.add_view(view=view1) - config.add_view(view=view2) - view = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(view(None, request), 'second') - - def test_add_view_with_permission(self): - view1 = lambda *arg: 'OK' - outerself = self - class DummyPolicy(object): - def effective_principals(self, r): - outerself.assertEqual(r, request) - return ['abc'] - def permits(self, context, principals, permission): - outerself.assertEqual(context, None) - outerself.assertEqual(principals, ['abc']) - outerself.assertEqual(permission, 'view') - return True - policy = DummyPolicy() - config = self._makeOne(authorization_policy=policy, - authentication_policy=policy) - config.add_view(view=view1, permission='view') - view = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(view(None, request), 'OK') - - def test_add_view_with_default_permission_no_explicit_permission(self): - view1 = lambda *arg: 'OK' - outerself = self - class DummyPolicy(object): - def effective_principals(self, r): - outerself.assertEqual(r, request) - return ['abc'] - def permits(self, context, principals, permission): - outerself.assertEqual(context, None) - outerself.assertEqual(principals, ['abc']) - outerself.assertEqual(permission, 'view') - return True - policy = DummyPolicy() - config = self._makeOne(authorization_policy=policy, - authentication_policy=policy, - default_permission='view') - config.add_view(view=view1) - view = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(view(None, request), 'OK') - - def test_add_view_with_no_default_permission_no_explicit_permission(self): - view1 = lambda *arg: 'OK' - class DummyPolicy(object): pass # wont be called - policy = DummyPolicy() - config = self._makeOne(authorization_policy=policy, - authentication_policy=policy) - config.add_view(view=view1) - view = self._getViewCallable(config) - request = self._makeRequest(config) - self.assertEqual(view(None, request), 'OK') - - def test_add_handler_action_in_route_pattern(self): - config = self._makeOne() - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_handler('name', '/:action', DummyHandler) - self._assertRoute(config, 'name', '/:action', 0) - self.assertEqual(len(views), 2) - - view = views[0] - preds = view['custom_predicates'] - self.assertEqual(len(preds), 1) - pred = preds[0] - request = DummyRequest() - self.assertEqual(pred(None, request), False) - request.matchdict = {'action':'action1'} - self.assertEqual(pred(None, request), True) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['attr'], 'action1') - self.assertEqual(view['view'], DummyHandler) - - view = views[1] - preds = view['custom_predicates'] - self.assertEqual(len(preds), 1) - pred = preds[0] - request = DummyRequest() - self.assertEqual(pred(None, request), False) - request.matchdict = {'action':'action2'} - self.assertEqual(pred(None, request), True) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['attr'], 'action2') - self.assertEqual(view['view'], DummyHandler) - - def test_add_handler_with_view_overridden_autoexpose_None(self): - config = self._makeOne() - views = [] - def dummy_add_view(**kw): - views.append(kw) # pragma: no cover - config.add_view = dummy_add_view - class MyView(DummyHandler): - __autoexpose__ = None - config.add_handler('name', '/:action', MyView) - self._assertRoute(config, 'name', '/:action', 0) - self.assertEqual(len(views), 0) - - def test_add_handler_with_view_overridden_autoexpose_broken_regex1(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - def dummy_add_view(**kw): - """ """ - config.add_view = dummy_add_view - class MyView(DummyHandler): - __autoexpose__ = 1 - self.assertRaises(ConfigurationError, config.add_handler, - 'name', '/{action}', MyView) - - def test_add_handler_with_view_overridden_autoexpose_broken_regex2(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - def dummy_add_view(**kw): - """ """ - config.add_view = dummy_add_view - class MyView(DummyHandler): - __autoexpose__ = 'a\\' - self.assertRaises(ConfigurationError, config.add_handler, - 'name', '/{action}', MyView) - - def test_add_handler_with_view_method_has_expose_config(self): - config = self._makeOne() - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - class MyView(object): - def action(self): # pragma: no cover - return 'response' - action.__exposed__ = [{'custom_predicates':(1,)}] - config.add_handler('name', '/:action', MyView) - self._assertRoute(config, 'name', '/:action', 0) - self.assertEqual(len(views), 1) - view = views[0] - preds = view['custom_predicates'] - self.assertEqual(len(preds), 2) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['attr'], 'action') - self.assertEqual(view['view'], MyView) - - def test_add_handler_with_view_method_has_expose_config_with_action(self): - config = self._makeOne() - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - class MyView(object): - def action(self): # pragma: no cover - return 'response' - action.__exposed__ = [{'name':'action3000'}] - config.add_handler('name', '/:action', MyView) - self._assertRoute(config, 'name', '/:action', 0) - self.assertEqual(len(views), 1) - view = views[0] - preds = view['custom_predicates'] - self.assertEqual(len(preds), 1) - pred = preds[0] - request = DummyRequest() - self.assertEqual(pred(None, request), False) - request.matchdict = {'action':'action3000'} - self.assertEqual(pred(None, request), True) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['attr'], 'action') - self.assertEqual(view['view'], MyView) - - def test_add_handler_with_view_method_has_expose_config_with_action_regex( - self): - config = self._makeOne() - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - class MyView(object): - def action(self): # pragma: no cover - return 'response' - action.__exposed__ = [{'name':'^action3000$'}] - config.add_handler('name', '/:action', MyView) - self._assertRoute(config, 'name', '/:action', 0) - self.assertEqual(len(views), 1) - view = views[0] - preds = view['custom_predicates'] - self.assertEqual(len(preds), 1) - pred = preds[0] - request = DummyRequest() - self.assertEqual(pred(None, request), False) - request.matchdict = {'action':'action3000'} - self.assertEqual(pred(None, request), True) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['attr'], 'action') - self.assertEqual(view['view'], MyView) - - def test_add_handler_doesnt_mutate_expose_dict(self): - config = self._makeOne() - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - exposed = [{'name':'^action3000$'}] - class MyView(object): - def action(self): # pragma: no cover - return 'response' - action.__exposed__ = exposed - config.add_handler('name', '/{action}', MyView) - self.assertEqual(exposed[0], {'name':'^action3000$'}) # not mutated - - def test_add_handler_with_action_and_action_in_path(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.add_handler, - 'name', '/{action}', DummyHandler, action='abc') - - def test_add_handler_with_explicit_action(self): - config = self._makeOne() - class DummyHandler(object): - def index(self): pass - index.__exposed__ = [{'a':'1'}] - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_handler('name', '/abc', DummyHandler, action='index') - self.assertEqual(len(views), 1) - view = views[0] - self.assertEqual(view['a'], '1') - self.assertEqual(view['attr'], 'index') - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['view'], DummyHandler) - - def test_add_handler_with_implicit_action(self): - config = self._makeOne() - class DummyHandler(object): - def __call__(self): pass - __call__.__exposed__ = [{'a':'1'}] - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_handler('name', '/abc', DummyHandler) - self.assertEqual(len(views), 1) - view = views[0] - self.assertEqual(view['a'], '1') - self.assertEqual(view['attr'], None) - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['view'], DummyHandler) - - def test_add_handler_with_multiple_action(self): - config = self._makeOne() - class DummyHandler(object): - def index(self): pass - def create(self): pass - create.__exposed__ = [{'name': 'index'}] - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_handler('name', '/abc', DummyHandler, action='index') - self.assertEqual(len(views), 2) - view = views[0] - self.assertEqual(view['attr'], 'create') - self.assertEqual(view['route_name'], 'name') - self.assertEqual(view['view'], DummyHandler) - view = views[1] - self.assertEqual(view['attr'], 'index') - - def test_add_handler_string(self): - import pyramid - views = [] - config = self._makeOne() - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_handler('name', '/abc', 'pyramid') - self.assertEqual(len(views), 1) - view = views[0] - self.assertEqual(view['view'], pyramid) - - def test_add_handler_pattern_None_no_previous_route(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.add_handler, - 'name', None, 'pyramid') - - def test_add_handler_pattern_None_with_previous_route(self): - import pyramid - config = self._makeOne() - config.add_route('name', ':def') - views = [] - def dummy_add_view(**kw): - views.append(kw) - config.add_view = dummy_add_view - config.add_route = None # shouldn't be called - config.add_handler('name', None, 'pyramid') - self.assertEqual(len(views), 1) - view = views[0] - self.assertEqual(view['view'], pyramid) - - - 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 test_get_routes_mapper_not_yet_registered(self): - config = self._makeOne() - mapper = config.get_routes_mapper() - self.assertEqual(mapper.routelist, []) - - def test_get_routes_mapper_already_registered(self): - from pyramid.interfaces import IRoutesMapper - config = self._makeOne() - mapper = object() - config.registry.registerUtility(mapper, IRoutesMapper) - result = config.get_routes_mapper() - self.assertEqual(result, mapper) - - def test_add_route_defaults(self): - config = self._makeOne() - route = config.add_route('name', 'path') - self._assertRoute(config, 'name', 'path') - self.assertEqual(route.name, 'name') - - def test_add_route_with_factory(self): - config = self._makeOne() - factory = object() - route = config.add_route('name', 'path', factory=factory) - self.assertEqual(route.factory, factory) - - def test_add_route_with_factory_dottedname(self): - config = self._makeOne() - route = config.add_route( - 'name', 'path', - factory='pyramid.tests.test_configuration.dummyfactory') - self.assertEqual(route.factory, dummyfactory) - - def test_add_route_with_xhr(self): - config = self._makeOne() - config.add_route('name', 'path', xhr=True) - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.is_xhr = True - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.is_xhr = False - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_request_method(self): - config = self._makeOne() - config.add_route('name', 'path', request_method='GET') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.method = 'GET' - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.method = 'POST' - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_path_info(self): - config = self._makeOne() - config.add_route('name', 'path', path_info='/foo') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.path_info = '/foo' - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.path_info = '/' - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_request_param(self): - config = self._makeOne() - config.add_route('name', 'path', request_param='abc') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.params = {'abc':'123'} - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.params = {} - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_custom_predicates(self): - config = self._makeOne() - def pred1(context, request): pass - def pred2(context, request): pass - config.add_route('name', 'path', custom_predicates=(pred1, pred2)) - route = self._assertRoute(config, 'name', 'path', 2) - self.assertEqual(route.predicates, [pred1, pred2]) - - def test_add_route_with_header(self): - config = self._makeOne() - config.add_route('name', 'path', header='Host') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.headers = {'Host':'example.com'} - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.headers = {} - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_accept(self): - config = self._makeOne() - config.add_route('name', 'path', accept='text/xml') - route = self._assertRoute(config, 'name', 'path', 1) - predicate = route.predicates[0] - request = self._makeRequest(config) - request.accept = ['text/xml'] - self.assertEqual(predicate(None, request), True) - request = self._makeRequest(config) - request.accept = ['text/html'] - self.assertEqual(predicate(None, request), False) - - def test_add_route_with_view(self): - config = self._makeOne() - 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() - 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() - 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() - 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() - 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() - 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() - 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') - self.assertEqual(wrapper(None, None), 'OK') - - def test_add_route_with_view_renderer_alias(self): - config = self._makeOne() - 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() - 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.failUnless(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() - 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.failUnless(hasattr(wrapper, '__call_permissive__')) - - def test_add_route_no_pattern_with_path(self): + def test_autocommit_true(self): config = self._makeOne() - route = config.add_route('name', path='path') - self._assertRoute(config, 'name', 'path') - self.assertEqual(route.name, 'name') - - def test_add_route_no_path_no_pattern(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.add_route, 'name') - - def test_add_route_with_pregenerator(self): - config = self._makeOne() - route = config.add_route('name', 'pattern', pregenerator='123') - self.assertEqual(route.pregenerator, '123') - - def test__override_not_yet_registered(self): - from pyramid.interfaces import IPackageOverrides - package = DummyPackage('package') - opackage = DummyPackage('opackage') - config = self._makeOne() - config._override(package, 'path', opackage, 'oprefix', - PackageOverrides=DummyOverrides) - overrides = config.registry.queryUtility(IPackageOverrides, - name='package') - self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) - self.assertEqual(overrides.package, package) - - def test__override_already_registered(self): - from pyramid.interfaces import IPackageOverrides - package = DummyPackage('package') - opackage = DummyPackage('opackage') - overrides = DummyOverrides(package) - config = self._makeOne() - config.registry.registerUtility(overrides, IPackageOverrides, - name='package') - config._override(package, 'path', opackage, 'oprefix', - PackageOverrides=DummyOverrides) - self.assertEqual(overrides.inserted, [('path', 'opackage', 'oprefix')]) - self.assertEqual(overrides.package, package) - - def test_add_static_here_no_utility_registered(self): - from pyramid.static import PackageURLParser - from zope.interface import implementedBy - from pyramid.static import StaticURLInfo - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - config = self._makeOne() - config.add_static_view('static', 'fixtures/static') - request_type = self._getRouteRequestIface(config, 'static/') - route = self._assertRoute(config, 'static/', 'static/*subpath') - self.assertEqual(route.factory.__class__, type(lambda x: x)) - iface = implementedBy(StaticURLInfo) - wrapped = config.registry.adapters.lookup( - (IViewClassifier, request_type, iface), IView, name='') - request = self._makeRequest(config) - self.assertEqual(wrapped(None, request).__class__, PackageURLParser) - - def test_add_static_view_package_relative(self): - from pyramid.interfaces import IStaticURLInfo - info = DummyStaticURLInfo() - config = self._makeOne() - config.registry.registerUtility(info, IStaticURLInfo) - config.add_static_view('static', 'pyramid.tests:fixtures/static') - self.assertEqual(info.added, - [('static', 'pyramid.tests:fixtures/static', {})]) - - def test_add_static_view_package_here_relative(self): - from pyramid.interfaces import IStaticURLInfo - info = DummyStaticURLInfo() - config = self._makeOne() - config.registry.registerUtility(info, IStaticURLInfo) - config.add_static_view('static', 'fixtures/static') - self.assertEqual(info.added, - [('static', 'pyramid.tests:fixtures/static', {})]) - - def test_add_static_view_absolute(self): - import os - from pyramid.interfaces import IStaticURLInfo - info = DummyStaticURLInfo() - config = self._makeOne() - config.registry.registerUtility(info, IStaticURLInfo) - here = os.path.dirname(__file__) - static_path = os.path.join(here, 'fixtures', 'static') - config.add_static_view('static', static_path) - self.assertEqual(info.added, - [('static', static_path, {})]) - - def test_set_notfound_view(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.exceptions import NotFound - config = self._makeOne() - view = lambda *arg: arg - config.set_notfound_view(view) - request = self._makeRequest(config) - view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, (None, request)) - - def test_set_notfound_view_request_has_context(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.exceptions import NotFound - config = self._makeOne() - view = lambda *arg: arg - config.set_notfound_view(view) - request = self._makeRequest(config) - request.context = 'abc' - view = self._getViewCallable(config, ctx_iface=implementedBy(NotFound), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, ('abc', request)) - - def test_set_forbidden_view(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.exceptions import Forbidden - config = self._makeOne() - view = lambda *arg: 'OK' - config.set_forbidden_view(view) - request = self._makeRequest(config) - view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, 'OK') - - def test_set_forbidden_view_request_has_context(self): - from zope.interface import implementedBy - from pyramid.interfaces import IRequest - from pyramid.exceptions import Forbidden - config = self._makeOne() - view = lambda *arg: arg - config.set_forbidden_view(view) - request = self._makeRequest(config) - request.context = 'abc' - view = self._getViewCallable(config, ctx_iface=implementedBy(Forbidden), - request_iface=IRequest) - result = view(None, request) - self.assertEqual(result, ('abc', request)) - - def test__set_authentication_policy(self): - from pyramid.interfaces import IAuthenticationPolicy - config = self._makeOne() - policy = object() - config._set_authentication_policy(policy) - self.assertEqual( - config.registry.getUtility(IAuthenticationPolicy), policy) - - def test__set_authorization_policy(self): - from pyramid.interfaces import IAuthorizationPolicy - config = self._makeOne() - policy = object() - config._set_authorization_policy(policy) - self.assertEqual( - config.registry.getUtility(IAuthorizationPolicy), policy) - - def test_set_locale_negotiator(self): - from pyramid.interfaces import ILocaleNegotiator - config = self._makeOne() - def negotiator(request): pass - config.set_locale_negotiator(negotiator) - self.assertEqual(config.registry.getUtility(ILocaleNegotiator), - negotiator) - - def test_set_locale_negotiator_dottedname(self): - from pyramid.interfaces import ILocaleNegotiator - config = self._makeOne() - config.set_locale_negotiator( - 'pyramid.tests.test_configuration.dummyfactory') - self.assertEqual(config.registry.getUtility(ILocaleNegotiator), - dummyfactory) - - def test_set_request_factory(self): - from pyramid.interfaces import IRequestFactory - config = self._makeOne() - factory = object() - config.set_request_factory(factory) - self.assertEqual(config.registry.getUtility(IRequestFactory), factory) - - def test_set_request_factory_dottedname(self): - from pyramid.interfaces import IRequestFactory - config = self._makeOne() - config.set_request_factory( - 'pyramid.tests.test_configuration.dummyfactory') - self.assertEqual(config.registry.getUtility(IRequestFactory), - dummyfactory) - - def test_set_renderer_globals_factory(self): - from pyramid.interfaces import IRendererGlobalsFactory - config = self._makeOne() - factory = object() - config.set_renderer_globals_factory(factory) - self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), - factory) - - def test_set_renderer_globals_factory_dottedname(self): - from pyramid.interfaces import IRendererGlobalsFactory - config = self._makeOne() - config.set_renderer_globals_factory( - 'pyramid.tests.test_configuration.dummyfactory') - self.assertEqual(config.registry.getUtility(IRendererGlobalsFactory), - dummyfactory) - - def test_set_default_permission(self): - from pyramid.interfaces import IDefaultPermission - config = self._makeOne() - config.set_default_permission('view') - self.assertEqual(config.registry.getUtility(IDefaultPermission), - 'view') - - def test_set_session_factory(self): - from pyramid.interfaces import ISessionFactory - config = self._makeOne() - config.set_session_factory('factory') - self.assertEqual(config.registry.getUtility(ISessionFactory), - 'factory') - - def test_add_translation_dirs_missing_dir(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, - config.add_translation_dirs, - '/wont/exist/on/my/system') - - def test_add_translation_dirs_resource_spec(self): - import os - from pyramid.interfaces import ITranslationDirectories - config = self._makeOne() - config.add_translation_dirs('pyramid.tests.localeapp:locale') - here = os.path.dirname(__file__) - locale = os.path.join(here, 'localeapp', 'locale') - self.assertEqual(config.registry.getUtility(ITranslationDirectories), - [locale]) - - def test_add_translation_dirs_registers_chameleon_translate(self): - from pyramid.interfaces import IChameleonTranslate - from pyramid.threadlocal import manager - request = DummyRequest() - config = self._makeOne() - manager.push({'request':request, 'registry':config.registry}) - try: - config.add_translation_dirs('pyramid.tests.localeapp:locale') - translate = config.registry.getUtility(IChameleonTranslate) - self.assertEqual(translate('Approve'), u'Approve') - finally: - manager.pop() - - def test_add_translation_dirs_abspath(self): - import os - from pyramid.interfaces import ITranslationDirectories - config = self._makeOne() - here = os.path.dirname(__file__) - locale = os.path.join(here, 'localeapp', 'locale') - config.add_translation_dirs(locale) - self.assertEqual(config.registry.getUtility(ITranslationDirectories), - [locale]) - - def test_derive_view_function(self): - def view(request): - return 'OK' - config = self._makeOne() - result = config.derive_view(view) - self.failIf(result is view) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_dottedname(self): - config = self._makeOne() - result = config.derive_view( - 'pyramid.tests.test_configuration.dummy_view') - self.failIf(result is dummy_view) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_with_renderer(self): - def view(request): - return 'OK' - config = self._makeOne() - class moo(object): - def __init__(self, *arg, **kw): - pass - def __call__(self, *arg, **kw): - return 'moo' - config.add_renderer('moo', moo) - result = config.derive_view(view, renderer='moo') - self.failIf(result is view) - self.assertEqual(result(None, None).body, 'moo') - - def test_derive_view_with_default_renderer_no_explicit_renderer(self): - def view(request): - return 'OK' - config = self._makeOne() - class moo(object): - def __init__(self, *arg, **kw): - pass - def __call__(self, *arg, **kw): - return 'moo' - config.add_renderer(None, moo) - result = config.derive_view(view) - self.failIf(result is view) - self.assertEqual(result(None, None).body, 'moo') - - def test_derive_view_with_default_renderer_with_explicit_renderer(self): - def view(request): - return 'OK' - config = self._makeOne() - class moo(object): pass - class foo(object): - def __init__(self, *arg, **kw): - pass - def __call__(self, *arg, **kw): - return 'foo' - config.add_renderer(None, moo) - config.add_renderer('foo', foo) - result = config.derive_view(view, renderer='foo') - self.failIf(result is view) - self.assertEqual(result(None, None).body, 'foo') - - def test_derive_view_class_without_attr(self): - class View(object): - def __init__(self, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config.derive_view(View) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_class_with_attr(self): - class View(object): - def __init__(self, request): - pass - def another(self): - return 'OK' - config = self._makeOne() - result = config.derive_view(View, attr='another') - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_function_context_and_request(self): - def view(context, request): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failUnless(result is view) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(view(None, None), 'OK') - - def test__derive_view_as_function_requestonly(self): - def view(request): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_newstyle_class_context_and_request(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_newstyle_class_requestonly(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_oldstyle_class_context_and_request(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_oldstyle_class_requestonly(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_instance_context_and_request(self): - class View: - def __call__(self, context, request): - return 'OK' - view = View() - config = self._makeOne() - result = config._derive_view(view) - self.failUnless(result is view) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_instance_requestonly(self): - class View: - def __call__(self, request): - return 'OK' - view = View() - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_with_debug_authorization_no_authpol(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - self.assertEqual(len(logger.messages), 1) - self.assertEqual(logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): Allowed " - "(no authorization policy in use)") - - def test__derive_view_with_debug_authorization_no_permission(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - self._registerSecurityPolicy(config, True) - logger = self._registerLogger(config) - result = config._derive_view(view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - self.assertEqual(len(logger.messages), 1) - self.assertEqual(logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): Allowed (" - "no permission registered)") - - def test__derive_view_debug_auth_permission_authpol_permitted(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, debug_authorization=True, - reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, True) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result.__call_permissive__, view) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - self.assertEqual(len(logger.messages), 1) - self.assertEqual(logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): True") - - def test__derive_view_debug_auth_permission_authpol_denied(self): - from pyramid.exceptions import Forbidden - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result.__call_permissive__, view) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertRaises(Forbidden, result, None, request) - self.assertEqual(len(logger.messages), 1) - self.assertEqual(logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): False") - - def test__derive_view_debug_auth_permission_authpol_denied2(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - permitted = result.__permitted__(None, None) - self.assertEqual(permitted, False) - - def test__derive_view_debug_auth_permission_authpol_overridden(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = config._derive_view(view, - permission='__no_permission_required__') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - self.assertEqual(len(logger.messages), 1) - self.assertEqual(logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): False") - - def test__derive_view_with_predicates_all(self): - view = lambda *arg: 'OK' - predicates = [] - def predicate1(context, request): - predicates.append(True) - return True - def predicate2(context, request): - predicates.append(True) - return True - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - next = result(None, None) - self.assertEqual(next, 'OK') - self.assertEqual(predicates, [True, True]) - - def test__derive_view_with_predicates_checker(self): - view = lambda *arg: 'OK' - predicates = [] - def predicate1(context, request): - predicates.append(True) - return True - def predicate2(context, request): - predicates.append(True) - return True - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - next = result.__predicated__(None, None) - self.assertEqual(next, True) - self.assertEqual(predicates, [True, True]) - - def test__derive_view_with_predicates_notall(self): - from pyramid.exceptions import NotFound - view = lambda *arg: 'OK' - predicates = [] - def predicate1(context, request): - predicates.append(True) - return True - def predicate2(context, request): - predicates.append(True) - return False - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - self.assertRaises(NotFound, result, None, None) - self.assertEqual(predicates, [True, True]) - - def test__derive_view_with_wrapper_viewname(self): - from webob import Response - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - inner_response = Response('OK') - def inner_view(context, request): - return inner_response - def outer_view(context, request): - self.assertEqual(request.wrapped_response, inner_response) - self.assertEqual(request.wrapped_body, inner_response.body) - self.assertEqual(request.wrapped_view, inner_view) - return Response('outer ' + request.wrapped_body) - config = self._makeOne() - config.registry.registerAdapter( - outer_view, (IViewClassifier, None, None), IView, 'owrap') - result = config._derive_view(inner_view, viewname='inner', - wrapper_viewname='owrap') - self.failIf(result is inner_view) - self.assertEqual(inner_view.__module__, result.__module__) - self.assertEqual(inner_view.__doc__, result.__doc__) - request = self._makeRequest(config) - request.registry = config.registry - response = result(None, request) - self.assertEqual(response.body, 'outer OK') - - def test__derive_view_with_wrapper_viewname_notfound(self): - from webob import Response - inner_response = Response('OK') - def inner_view(context, request): - return inner_response - config = self._makeOne() - request = self._makeRequest(config) - request.registry = config.registry - wrapped = config._derive_view( - inner_view, viewname='inner', wrapper_viewname='owrap') - self.assertRaises(ValueError, wrapped, None, request) - - def test_override_resource_samename(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.override_resource,'a', 'a') - - def test_override_resource_directory_with_file(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.override_resource, - 'a:foo/', 'a:foo.pt') - - def test_override_resource_file_with_directory(self): - from pyramid.exceptions import ConfigurationError - config = self._makeOne() - self.assertRaises(ConfigurationError, config.override_resource, - 'a:foo.pt', 'a:foo/') - - def test_override_resource_success(self): - config = self._makeOne() - override = DummyUnderOverride() - config.override_resource( - 'pyramid.tests.fixtureapp:templates/foo.pt', - 'pyramid.tests.fixtureapp.subpackage:templates/bar.pt', - _override=override) - from pyramid.tests import fixtureapp - from pyramid.tests.fixtureapp import subpackage - self.assertEqual(override.package, fixtureapp) - self.assertEqual(override.path, 'templates/foo.pt') - self.assertEqual(override.override_package, subpackage) - self.assertEqual(override.override_prefix, 'templates/bar.pt') - - def test_add_renderer(self): - from pyramid.interfaces import IRendererFactory - config = self._makeOne() - renderer = object() - config.add_renderer('name', renderer) - self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), - renderer) - - def test_add_renderer_dottedname_factory(self): - from pyramid.interfaces import IRendererFactory - config = self._makeOne() - import pyramid.tests - config.add_renderer('name', 'pyramid.tests') - self.assertEqual(config.registry.getUtility(IRendererFactory, 'name'), - pyramid.tests) - - def test_scan_integration(self): - import os - from zope.interface import alsoProvides - from pyramid.interfaces import IRequest - from pyramid.view import render_view_to_response - import pyramid.tests.grokkedapp as package - config = self._makeOne() - config.scan(package) - - ctx = DummyContext() - req = DummyRequest() - alsoProvides(req, IRequest) - req.registry = config.registry - - req.method = 'GET' - result = render_view_to_response(ctx, req, '') - self.assertEqual(result, 'grokked') - - req.method = 'POST' - result = render_view_to_response(ctx, req, '') - self.assertEqual(result, 'grokked_post') - - result= render_view_to_response(ctx, req, 'grokked_class') - self.assertEqual(result, 'grokked_class') - - result= render_view_to_response(ctx, req, 'grokked_instance') - self.assertEqual(result, 'grokked_instance') - - result= render_view_to_response(ctx, req, 'oldstyle_grokked_class') - self.assertEqual(result, 'oldstyle_grokked_class') - - req.method = 'GET' - result = render_view_to_response(ctx, req, 'another') - self.assertEqual(result, 'another_grokked') - - req.method = 'POST' - result = render_view_to_response(ctx, req, 'another') - self.assertEqual(result, 'another_grokked_post') - - result= render_view_to_response(ctx, req, 'another_grokked_class') - self.assertEqual(result, 'another_grokked_class') - - result= render_view_to_response(ctx, req, 'another_grokked_instance') - self.assertEqual(result, 'another_grokked_instance') - - result= render_view_to_response(ctx, req, - 'another_oldstyle_grokked_class') - self.assertEqual(result, 'another_oldstyle_grokked_class') - - result = render_view_to_response(ctx, req, 'stacked1') - self.assertEqual(result, 'stacked') - - result = render_view_to_response(ctx, req, 'stacked2') - self.assertEqual(result, 'stacked') - - result = render_view_to_response(ctx, req, 'another_stacked1') - self.assertEqual(result, 'another_stacked') - - result = render_view_to_response(ctx, req, 'another_stacked2') - self.assertEqual(result, 'another_stacked') - - result = render_view_to_response(ctx, req, 'stacked_class1') - self.assertEqual(result, 'stacked_class') - - result = render_view_to_response(ctx, req, 'stacked_class2') - self.assertEqual(result, 'stacked_class') - - result = render_view_to_response(ctx, req, 'another_stacked_class1') - self.assertEqual(result, 'another_stacked_class') - - result = render_view_to_response(ctx, req, 'another_stacked_class2') - self.assertEqual(result, 'another_stacked_class') - - if not os.name.startswith('java'): - # on Jython, a class without an __init__ apparently accepts - # any number of arguments without raising a TypeError. - - self.assertRaises(TypeError, - render_view_to_response, ctx, req, 'basemethod') - - result = render_view_to_response(ctx, req, 'method1') - self.assertEqual(result, 'method1') - - result = render_view_to_response(ctx, req, 'method2') - self.assertEqual(result, 'method2') - - result = render_view_to_response(ctx, req, 'stacked_method1') - self.assertEqual(result, 'stacked_method') - - result = render_view_to_response(ctx, req, 'stacked_method2') - self.assertEqual(result, 'stacked_method') - - result = render_view_to_response(ctx, req, 'subpackage_init') - self.assertEqual(result, 'subpackage_init') - - result = render_view_to_response(ctx, req, 'subpackage_notinit') - self.assertEqual(result, 'subpackage_notinit') - - result = render_view_to_response(ctx, req, 'subsubpackage_init') - self.assertEqual(result, 'subsubpackage_init') - - result = render_view_to_response(ctx, req, 'pod_notinit') - self.assertEqual(result, None) - - def test_scan_integration_dottedname_package(self): - from zope.interface import alsoProvides - from pyramid.interfaces import IRequest - from pyramid.view import render_view_to_response - config = self._makeOne() - config.scan('pyramid.tests.grokkedapp') - - ctx = DummyContext() - req = DummyRequest() - alsoProvides(req, IRequest) - req.registry = config.registry - - req.method = 'GET' - result = render_view_to_response(ctx, req, '') - self.assertEqual(result, 'grokked') - - def test_testing_securitypolicy(self): - from pyramid.testing import DummySecurityPolicy - config = self._makeOne() - config.testing_securitypolicy('user', ('group1', 'group2'), - permissive=False) - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy - ut = config.registry.getUtility(IAuthenticationPolicy) - self.failUnless(isinstance(ut, DummySecurityPolicy)) - ut = config.registry.getUtility(IAuthorizationPolicy) - self.assertEqual(ut.userid, 'user') - self.assertEqual(ut.groupids, ('group1', 'group2')) - self.assertEqual(ut.permissive, False) - - def test_testing_models(self): - from pyramid.traversal import find_model - from pyramid.interfaces import ITraverser - ob1 = object() - ob2 = object() - models = {'/ob1':ob1, '/ob2':ob2} - config = self._makeOne() - config.testing_models(models) - adapter = config.registry.getAdapter(None, ITraverser) - result = adapter({'PATH_INFO':'/ob1'}) - self.assertEqual(result['context'], ob1) - self.assertEqual(result['view_name'], '') - self.assertEqual(result['subpath'], ()) - self.assertEqual(result['traversed'], (u'ob1',)) - self.assertEqual(result['virtual_root'], ob1) - self.assertEqual(result['virtual_root_path'], ()) - result = adapter({'PATH_INFO':'/ob2'}) - self.assertEqual(result['context'], ob2) - self.assertEqual(result['view_name'], '') - self.assertEqual(result['subpath'], ()) - self.assertEqual(result['traversed'], (u'ob2',)) - self.assertEqual(result['virtual_root'], ob2) - self.assertEqual(result['virtual_root_path'], ()) - self.assertRaises(KeyError, adapter, {'PATH_INFO':'/ob3'}) - try: - config.begin() - self.assertEqual(find_model(None, '/ob1'), ob1) - finally: - config.end() - - def test_testing_add_subscriber_single(self): - config = self._makeOne() - L = config.testing_add_subscriber(IDummy) - event = DummyEvent() - config.registry.notify(event) - self.assertEqual(len(L), 1) - self.assertEqual(L[0], event) - config.registry.notify(object()) - self.assertEqual(len(L), 1) - - def test_testing_add_subscriber_dottedname(self): - config = self._makeOne() - L = config.testing_add_subscriber( - 'pyramid.tests.test_configuration.IDummy') - event = DummyEvent() - config.registry.notify(event) - self.assertEqual(len(L), 1) - self.assertEqual(L[0], event) - config.registry.notify(object()) - self.assertEqual(len(L), 1) - - def test_testing_add_subscriber_multiple(self): - config = self._makeOne() - L = config.testing_add_subscriber((Interface, IDummy)) - event = DummyEvent() - event.object = 'foo' - # the below is the equivalent of z.c.event.objectEventNotify(event) - config.registry.subscribers((event.object, event), None) - self.assertEqual(len(L), 2) - self.assertEqual(L[0], 'foo') - self.assertEqual(L[1], event) - - def test_testing_add_subscriber_defaults(self): - config = self._makeOne() - L = config.testing_add_subscriber() - event = object() - config.registry.notify(event) - self.assertEqual(L[-1], event) - event2 = object() - config.registry.notify(event2) - self.assertEqual(L[-1], event2) - - def test_hook_zca(self): - from pyramid.threadlocal import get_current_registry - gsm = DummyGetSiteManager() - config = self._makeOne() - config.hook_zca(getSiteManager=gsm) - self.assertEqual(gsm.hook, get_current_registry) - - def test_unhook_zca(self): - gsm = DummyGetSiteManager() - config = self._makeOne() - config.unhook_zca(getSiteManager=gsm) - self.assertEqual(gsm.unhooked, True) - - def test_testing_add_renderer(self): - config = self._makeOne() - renderer = config.testing_add_renderer('templates/foo.pt') - from pyramid.testing import DummyTemplateRenderer - self.failUnless(isinstance(renderer, 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) - renderer.assert_(foo=1) - renderer.assert_(bar=2) - renderer.assert_(request=request) - - def test_testing_add_renderer_explicitrenderer(self): - config = self._makeOne() - class E(Exception): pass - def renderer(kw, system): - self.assertEqual(kw, {'foo':1, 'bar':2}) - raise E - renderer = config.testing_add_renderer('templates/foo.pt', renderer) - from pyramid.renderers import render_to_response - # must provide request to pass in registry (this is a functest) - request = DummyRequest() - request.registry = config.registry - try: - render_to_response( - 'templates/foo.pt', {'foo':1, 'bar':2}, request=request) - except E: - pass - else: # pragma: no cover - raise AssertionError - - def test_testing_add_template(self): - config = self._makeOne() - renderer = config.testing_add_template('templates/foo.pt') - from pyramid.testing import DummyTemplateRenderer - self.failUnless(isinstance(renderer, 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', dict(foo=1, bar=2), - request=request) - renderer.assert_(foo=1) - renderer.assert_(bar=2) - renderer.assert_(request=request) - -class Test__map_view(unittest.TestCase): - def setUp(self): - from pyramid.registry import Registry - self.registry = Registry() - testing.setUp(registry=self.registry) - - def tearDown(self): - del self.registry - testing.tearDown() - - def _registerRenderer(self, typ='.txt'): - from pyramid.interfaces import IRendererFactory - from pyramid.interfaces import ITemplateRenderer - from zope.interface import implements - class Renderer: - implements(ITemplateRenderer) - spec = 'abc' + typ - def __init__(self, path): - self.__class__.path = path - def __call__(self, *arg): - return 'Hello!' - self.registry.registerUtility(Renderer, IRendererFactory, name=typ) - return Renderer - - def _makeRequest(self): - request = DummyRequest() - request.registry = self.registry - return request - - def _callFUT(self, view, **kw): - from pyramid.configuration import _map_view - return _map_view(view, self.registry, **kw) - - def test__map_view_as_function_context_and_request(self): - def view(context, request): - return 'OK' - result = self._callFUT(view) - self.failUnless(result is view) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_function_with_attr(self): - def view(context, request): - """ """ - result = self._callFUT(view, attr='__name__') - self.failIf(result is view) - self.assertRaises(TypeError, result, None, None) - - def test__map_view_as_function_with_attr_and_renderer(self): - renderer = self._registerRenderer() - view = lambda *arg: 'OK' - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='__name__', renderer=info) - self.failIf(result is view) - self.assertRaises(TypeError, result, None, None) - - def test__map_view_as_function_requestonly(self): - def view(request): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_function_requestonly_with_attr(self): - def view(request): - """ """ - result = self._callFUT(view, attr='__name__') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertRaises(TypeError, result, None, None) - - def test__map_view_as_newstyle_class_context_and_request(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_context_and_request_with_attr(self): - class view(object): - def __init__(self, context, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_context_and_request_attr_and_renderer( - self): - renderer = self._registerRenderer() - class view(object): - def __init__(self, context, request): - pass - def index(self): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_newstyle_class_requestonly(self): - class view(object): - def __init__(self, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_requestonly_with_attr(self): - class view(object): - def __init__(self, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_newstyle_class_requestonly_attr_and_renderer(self): - renderer = self._registerRenderer() - class view(object): - def __init__(self, request): - pass - def index(self): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_oldstyle_class_context_and_request(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_class_context_and_request_with_attr(self): - class view: - def __init__(self, context, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_cls_context_request_attr_and_renderer(self): - renderer = self._registerRenderer() - class view: - def __init__(self, context, request): - pass - def index(self): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_oldstyle_class_requestonly(self): - class view: - def __init__(self, request): - pass - def __call__(self): - return 'OK' - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_class_requestonly_with_attr(self): - class view: - def __init__(self, request): - pass - def index(self): - return 'OK' - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_oldstyle_class_requestonly_attr_and_renderer(self): - renderer = self._registerRenderer() - class view: - def __init__(self, request): - pass - def index(self): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_instance_context_and_request(self): - class View: - def __call__(self, context, request): - return 'OK' - view = View() - result = self._callFUT(view) - self.failUnless(result is view) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_context_and_request_and_attr(self): - class View: - def index(self, context, request): - return 'OK' - view = View() - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_context_and_request_attr_and_renderer(self): - renderer = self._registerRenderer() - class View: - def index(self, context, request): - return {'a':'1'} - view = View() - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_as_instance_requestonly(self): - class View: - def __call__(self, request): - return 'OK' - view = View() - result = self._callFUT(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_requestonly_with_attr(self): - class View: - def index(self, request): - return 'OK' - view = View() - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_requestonly_with_attr_and_renderer(self): - renderer = self._registerRenderer() - class View: - def index(self, request): - return {'a':'1'} - view = View() - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, attr='index', renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_rendereronly(self): - renderer = self._registerRenderer() - def view(context, request): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_with_registry(self): - renderer = self._registerRenderer() - def view(context, request): - return {'a':'1'} - info = {'name':renderer.spec, 'package':None} - result = self._callFUT(view, renderer=info) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - -class Test_decorate_view(unittest.TestCase): - def _callFUT(self, wrapped, original): - from pyramid.configuration import decorate_view - return decorate_view(wrapped, original) - - def test_it_same(self): - def view(context, request): - """ """ - result = self._callFUT(view, view) - self.assertEqual(result, False) - - def test_it_different(self): - class DummyView1: - """ 1 """ - __name__ = '1' - __module__ = '1' - def __call__(self, context, request): - """ """ - def __call_permissive__(self, context, reuqest): - """ """ - def __predicated__(self, context, reuqest): - """ """ - def __permitted__(self, context, request): - """ """ - class DummyView2: - """ 2 """ - __name__ = '2' - __module__ = '2' - def __call__(self, context, request): - """ """ - def __call_permissive__(self, context, reuqest): - """ """ - def __predicated__(self, context, reuqest): - """ """ - def __permitted__(self, context, request): - """ """ - view1 = DummyView1() - view2 = DummyView2() - result = self._callFUT(view1, view2) - self.assertEqual(result, True) - self.failUnless(view1.__doc__ is view2.__doc__) - self.failUnless(view1.__module__ is view2.__module__) - self.failUnless(view1.__name__ is view2.__name__) - self.failUnless(view1.__call_permissive__.im_func is - view2.__call_permissive__.im_func) - self.failUnless(view1.__permitted__.im_func is - view2.__permitted__.im_func) - self.failUnless(view1.__predicated__.im_func is - view2.__predicated__.im_func) - -class Test__make_predicates(unittest.TestCase): - def _callFUT(self, **kw): - from pyramid.configuration import _make_predicates - return _make_predicates(**kw) - - def test_ordering_xhr_and_request_method_trump_only_containment(self): - order1, _, _ = self._callFUT(xhr=True, request_method='GET') - order2, _, _ = self._callFUT(containment=True) - self.failUnless(order1 < order2) - - def test_ordering_number_of_predicates(self): - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - header='header', - accept='accept', - containment='containment', - request_type='request_type', - custom=('a',) - ) - order2, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - header='header', - accept='accept', - containment='containment', - request_type='request_type', - custom=('a',) - ) - order3, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - header='header', - accept='accept', - containment='containment', - request_type='request_type', - ) - order4, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - header='header', - accept='accept', - containment='containment', - ) - order5, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - header='header', - accept='accept', - ) - order6, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - header='header', - ) - order7, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - request_param='param', - ) - order8, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - ) - order9, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - ) - order10, _, _ = self._callFUT( - xhr='xhr', - ) - order11, _, _ = self._callFUT( - ) - self.assertEqual(order1, order2) - self.failUnless(order3 > order2) - self.failUnless(order4 > order3) - self.failUnless(order5 > order4) - self.failUnless(order6 > order5) - self.failUnless(order7 > order6) - self.failUnless(order8 > order7) - self.failUnless(order9 > order8) - self.failUnless(order10 > order9) - self.failUnless(order11 > order10) - - def test_ordering_importance_of_predicates(self): - order1, _, _ = self._callFUT( - xhr='xhr', - ) - order2, _, _ = self._callFUT( - request_method='request_method', - ) - order3, _, _ = self._callFUT( - path_info='path_info', - ) - order4, _, _ = self._callFUT( - request_param='param', - ) - order5, _, _ = self._callFUT( - header='header', - ) - order6, _, _ = self._callFUT( - accept='accept', - ) - order7, _, _ = self._callFUT( - containment='containment', - ) - order8, _, _ = self._callFUT( - request_type='request_type', - ) - order9, _, _ = self._callFUT( - custom=('a',), - ) - self.failUnless(order1 > order2) - self.failUnless(order2 > order3) - self.failUnless(order3 > order4) - self.failUnless(order4 > order5) - self.failUnless(order5 > order6) - self.failUnless(order6 > order7) - self.failUnless(order7 > order8) - self.failUnless(order8 > order9) - - def test_ordering_importance_and_number(self): - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - ) - order2, _, _ = self._callFUT( - custom=('a',), - ) - self.failUnless(order1 < order2) - - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - ) - order2, _, _ = self._callFUT( - request_method='request_method', - custom=('a',), - ) - self.failUnless(order1 > order2) - - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - ) - order2, _, _ = self._callFUT( - request_method='request_method', - custom=('a',), - ) - self.failUnless(order1 < order2) - - order1, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - path_info='path_info', - ) - order2, _, _ = self._callFUT( - xhr='xhr', - request_method='request_method', - custom=('a',), - ) - self.failUnless(order1 > order2) - - def test_different_custom_predicates_with_same_hash(self): - class PredicateWithHash(object): - def __hash__(self): - return 1 - a = PredicateWithHash() - b = PredicateWithHash() - _, _, a_phash = self._callFUT(custom=(a,)) - _, _, b_phash = self._callFUT(custom=(b,)) - self.assertEqual(a_phash, b_phash) - - def test_traverse_has_remainder_already(self): - order, predicates, phash = self._callFUT(traverse='/1/:a/:b') - self.assertEqual(len(predicates), 1) - pred = predicates[0] - info = {'traverse':'abc'} - request = DummyRequest() - result = pred(info, request) - self.assertEqual(result, True) - self.assertEqual(info, {'traverse':'abc'}) - - def test_traverse_matches(self): - order, predicates, phash = self._callFUT(traverse='/1/:a/:b') - self.assertEqual(len(predicates), 1) - pred = predicates[0] - info = {'match':{'a':'a', 'b':'b'}} - request = DummyRequest() - result = pred(info, request) - self.assertEqual(result, True) - self.assertEqual(info, {'match': - {'a':'a', 'b':'b', 'traverse':('1', 'a', 'b')}}) - -class TestMultiView(unittest.TestCase): - def _getTargetClass(self): - from pyramid.configuration import MultiView - return MultiView - - def _makeOne(self, name='name'): - return self._getTargetClass()(name) - - def test_class_implements_ISecuredView(self): - from zope.interface.verify import verifyClass - from pyramid.interfaces import ISecuredView - verifyClass(ISecuredView, self._getTargetClass()) - - def test_instance_implements_ISecuredView(self): - from zope.interface.verify import verifyObject - from pyramid.interfaces import ISecuredView - verifyObject(ISecuredView, self._makeOne()) - - def test_add(self): - mv = self._makeOne() - mv.add('view', 100) - self.assertEqual(mv.views, [(100, 'view', None)]) - mv.add('view2', 99) - self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)]) - mv.add('view3', 100, 'text/html') - self.assertEqual(mv.media_views['text/html'], [(100, 'view3', None)]) - mv.add('view4', 99, 'text/html') - self.assertEqual(mv.media_views['text/html'], - [(99, 'view4', None), (100, 'view3', None)]) - mv.add('view5', 100, 'text/xml') - self.assertEqual(mv.media_views['text/xml'], [(100, 'view5', None)]) - self.assertEqual(set(mv.accepts), set(['text/xml', 'text/html'])) - self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)]) - mv.add('view6', 98, 'text/*') - self.assertEqual(mv.views, [(98, 'view6', None), - (99, 'view2', None), - (100, 'view', None)]) - - def test_add_with_phash(self): - mv = self._makeOne() - mv.add('view', 100, phash='abc') - self.assertEqual(mv.views, [(100, 'view', 'abc')]) - mv.add('view', 100, phash='abc') - self.assertEqual(mv.views, [(100, 'view', 'abc')]) - mv.add('view', 100, phash='def') - self.assertEqual(mv.views, [(100, 'view', 'abc'), (100, 'view', 'def')]) - mv.add('view', 100, phash='abc') - self.assertEqual(mv.views, [(100, 'view', 'abc'), (100, 'view', 'def')]) - - def test_get_views_request_has_no_accept(self): - request = DummyRequest() - mv = self._makeOne() - mv.views = [(99, lambda *arg: None)] - self.assertEqual(mv.get_views(request), mv.views) - - def test_get_views_no_self_accepts(self): - request = DummyRequest() - request.accept = True - mv = self._makeOne() - mv.accepts = [] - mv.views = [(99, lambda *arg: None)] - self.assertEqual(mv.get_views(request), mv.views) - - def test_get_views(self): - request = DummyRequest() - request.accept = DummyAccept('text/html') - mv = self._makeOne() - mv.accepts = ['text/html'] - mv.views = [(99, lambda *arg: None)] - html_views = [(98, lambda *arg: None)] - mv.media_views['text/html'] = html_views - self.assertEqual(mv.get_views(request), html_views + mv.views) - - def test_get_views_best_match_returns_None(self): - request = DummyRequest() - request.accept = DummyAccept(None) - mv = self._makeOne() - mv.accepts = ['text/html'] - mv.views = [(99, lambda *arg: None)] - self.assertEqual(mv.get_views(request), mv.views) - - def test_match_not_found(self): - from pyramid.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, mv.match, context, request) - - def test_match_predicate_fails(self): - from pyramid.exceptions import NotFound - mv = self._makeOne() - def view(context, request): - """ """ - view.__predicated__ = lambda *arg: False - mv.views = [(100, view, None)] - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, mv.match, context, request) - - def test_match_predicate_succeeds(self): - mv = self._makeOne() - def view(context, request): - """ """ - view.__predicated__ = lambda *arg: True - mv.views = [(100, view, None)] - context = DummyContext() - request = DummyRequest() - result = mv.match(context, request) - self.assertEqual(result, view) - - def test_permitted_no_views(self): - from pyramid.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, mv.__permitted__, context, request) - - def test_permitted_no_match_with__permitted__(self): - mv = self._makeOne() - def view(context, request): - """ """ - mv.views = [(100, view, None)] - self.assertEqual(mv.__permitted__(None, None), True) - - def test_permitted(self): - mv = self._makeOne() - def view(context, request): - """ """ - def permitted(context, request): - return False - view.__permitted__ = permitted - mv.views = [(100, view, None)] - context = DummyContext() - request = DummyRequest() - result = mv.__permitted__(context, request) - self.assertEqual(result, False) - - def test__call__not_found(self): - from pyramid.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, mv, context, request) - - def test___call__intermediate_not_found(self): - from pyramid.exceptions import PredicateMismatch - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - request.view_name = '' - expected_response = DummyResponse() - def view1(context, request): - raise PredicateMismatch - def view2(context, request): - return expected_response - mv.views = [(100, view1, None), (99, view2, None)] - response = mv(context, request) - self.assertEqual(response, expected_response) - - def test___call__raise_not_found_isnt_interpreted_as_pred_mismatch(self): - from pyramid.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - request.view_name = '' - def view1(context, request): - raise NotFound - def view2(context, request): - """ """ - mv.views = [(100, view1, None), (99, view2, None)] - self.assertRaises(NotFound, mv, context, request) - - def test___call__(self): - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - request.view_name = '' - expected_response = DummyResponse() - def view(context, request): - return expected_response - mv.views = [(100, view, None)] - response = mv(context, request) - self.assertEqual(response, expected_response) - - def test__call_permissive__not_found(self): - from pyramid.exceptions import NotFound - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - self.assertRaises(NotFound, mv, context, request) - - def test___call_permissive_has_call_permissive(self): - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - request.view_name = '' - expected_response = DummyResponse() - def view(context, request): - """ """ - def permissive(context, request): - return expected_response - view.__call_permissive__ = permissive - mv.views = [(100, view, None)] - response = mv.__call_permissive__(context, request) - self.assertEqual(response, expected_response) - - def test___call_permissive_has_no_call_permissive(self): - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - request.view_name = '' - expected_response = DummyResponse() - def view(context, request): - return expected_response - mv.views = [(100, view, None)] - response = mv.__call_permissive__(context, request) - self.assertEqual(response, expected_response) - - def test__call__with_accept_match(self): - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - request.accept = DummyAccept('text/html', 'text/xml') - expected_response = DummyResponse() - def view(context, request): - return expected_response - mv.views = [(100, None)] - mv.media_views['text/xml'] = [(100, view, None)] - mv.accepts = ['text/xml'] - response = mv(context, request) - self.assertEqual(response, expected_response) - - def test__call__with_accept_miss(self): - mv = self._makeOne() - context = DummyContext() - request = DummyRequest() - request.accept = DummyAccept('text/plain', 'text/html') - expected_response = DummyResponse() - def view(context, request): - return expected_response - mv.views = [(100, view, None)] - mv.media_views['text/xml'] = [(100, None, None)] - mv.accepts = ['text/xml'] - response = mv(context, request) - self.assertEqual(response, expected_response) - - -class TestRequestOnly(unittest.TestCase): - def _callFUT(self, arg): - from pyramid.configuration import requestonly - return requestonly(arg) - - def test_newstyle_class_no_init(self): - class foo(object): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_newstyle_class_init_toomanyargs(self): - class foo(object): - def __init__(self, context, request): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_newstyle_class_init_onearg_named_request(self): - class foo(object): - def __init__(self, request): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_newstyle_class_init_onearg_named_somethingelse(self): - class foo(object): - def __init__(self, req): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_newstyle_class_init_defaultargs_firstname_not_request(self): - class foo(object): - def __init__(self, context, request=None): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_newstyle_class_init_defaultargs_firstname_request(self): - class foo(object): - def __init__(self, request, foo=1, bar=2): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_newstyle_class_init_noargs(self): - class foo(object): - def __init__(): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_oldstyle_class_no_init(self): - class foo: - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_oldstyle_class_init_toomanyargs(self): - class foo: - def __init__(self, context, request): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_oldstyle_class_init_onearg_named_request(self): - class foo: - def __init__(self, request): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_oldstyle_class_init_onearg_named_somethingelse(self): - class foo: - def __init__(self, req): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_oldstyle_class_init_defaultargs_firstname_not_request(self): - class foo: - def __init__(self, context, request=None): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_oldstyle_class_init_defaultargs_firstname_request(self): - class foo: - def __init__(self, request, foo=1, bar=2): - """ """ - self.assertTrue(self._callFUT(foo), True) - - def test_oldstyle_class_init_noargs(self): - class foo: - def __init__(): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_function_toomanyargs(self): - def foo(context, request): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_function_onearg_named_request(self): - def foo(request): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_function_onearg_named_somethingelse(self): - def foo(req): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_function_defaultargs_firstname_not_request(self): - def foo(context, request=None): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_function_defaultargs_firstname_request(self): - def foo(request, foo=1, bar=2): - """ """ - self.assertTrue(self._callFUT(foo)) - - def test_function_noargs(self): - def foo(): - """ """ - self.assertFalse(self._callFUT(foo)) - - def test_instance_toomanyargs(self): - class Foo: - def __call__(self, context, request): - """ """ - foo = Foo() - self.assertFalse(self._callFUT(foo)) - - def test_instance_defaultargs_onearg_named_request(self): - class Foo: - def __call__(self, request): - """ """ - foo = Foo() - self.assertTrue(self._callFUT(foo)) - - def test_instance_defaultargs_onearg_named_somethingelse(self): - class Foo: - def __call__(self, req): - """ """ - foo = Foo() - self.assertTrue(self._callFUT(foo)) - - def test_instance_defaultargs_firstname_not_request(self): - class Foo: - def __call__(self, context, request=None): - """ """ - foo = Foo() - self.assertFalse(self._callFUT(foo)) - - def test_instance_defaultargs_firstname_request(self): - class Foo: - def __call__(self, request, foo=1, bar=2): - """ """ - foo = Foo() - self.assertTrue(self._callFUT(foo), True) - - def test_instance_nocall(self): - class Foo: pass - foo = Foo() - self.assertFalse(self._callFUT(foo)) - -class TestMakeApp(unittest.TestCase): - def setUp(self): - testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, *arg, **kw): - from pyramid.configuration import make_app - return make_app(*arg, **kw) - - def test_it(self): - settings = {'a':1} - rootfactory = object() - app = self._callFUT(rootfactory, settings=settings, - Configurator=DummyConfigurator) - self.assertEqual(app.root_factory, rootfactory) - self.assertEqual(app.settings, settings) - self.assertEqual(app.zcml_file, 'configure.zcml') - self.assertEqual(app.zca_hooked, True) - - def test_it_options_means_settings(self): - settings = {'a':1} - rootfactory = object() - app = self._callFUT(rootfactory, options=settings, - Configurator=DummyConfigurator) - self.assertEqual(app.root_factory, rootfactory) - self.assertEqual(app.settings, settings) - self.assertEqual(app.zcml_file, 'configure.zcml') - - def test_it_with_package(self): - package = object() - rootfactory = object() - app = self._callFUT(rootfactory, package=package, - Configurator=DummyConfigurator) - self.assertEqual(app.package, package) - - def test_it_with_custom_configure_zcml(self): - rootfactory = object() - settings = {'configure_zcml':'2.zcml'} - app = self._callFUT(rootfactory, filename='1.zcml', settings=settings, - Configurator=DummyConfigurator) - self.assertEqual(app.zcml_file, '2.zcml') - -class Test_isexception(unittest.TestCase): - def _callFUT(self, ob): - from pyramid.configuration import isexception - return isexception(ob) - - def test_is_exception_instance(self): - class E(Exception): - pass - e = E() - self.assertEqual(self._callFUT(e), True) - - def test_is_exception_class(self): - class E(Exception): - pass - self.assertEqual(self._callFUT(E), True) - - def test_is_IException(self): - from pyramid.interfaces import IException - self.assertEqual(self._callFUT(IException), True) - - def test_is_IException_subinterface(self): - from pyramid.interfaces import IException - class ISubException(IException): - pass - self.assertEqual(self._callFUT(ISubException), True) - -class TestActionPredicate(unittest.TestCase): - def _getTargetClass(self): - from pyramid.configuration import ActionPredicate - return ActionPredicate - - def _makeOne(self, action='myaction'): - return self._getTargetClass()(action) - - def test_bad_action_regex_string(self): - from pyramid.exceptions import ConfigurationError - cls = self._getTargetClass() - self.assertRaises(ConfigurationError, cls, '[a-z') - - def test_bad_action_regex_None(self): - from pyramid.exceptions import ConfigurationError - cls = self._getTargetClass() - self.assertRaises(ConfigurationError, cls, None) - - def test___call__no_matchdict(self): - pred = self._makeOne() - request = DummyRequest() - self.assertEqual(pred(None, request), False) - - def test___call__no_action_in_matchdict(self): - pred = self._makeOne() - request = DummyRequest() - request.matchdict = {} - self.assertEqual(pred(None, request), False) - - def test___call__action_does_not_match(self): - pred = self._makeOne() - request = DummyRequest() - request.matchdict = {'action':'notmyaction'} - self.assertEqual(pred(None, request), False) - - def test___call__action_matches(self): - pred = self._makeOne() - request = DummyRequest() - request.matchdict = {'action':'myaction'} - self.assertEqual(pred(None, request), True) - - def test___hash__(self): - pred1 = self._makeOne() - pred2 = self._makeOne() - pred3 = self._makeOne(action='notthesame') - self.assertEqual(hash(pred1), hash(pred2)) - self.assertNotEqual(hash(pred1), hash(pred3)) - self.assertNotEqual(hash(pred2), hash(pred3)) - - - -class DummyRequest: - subpath = () - matchdict = None - def __init__(self): - self.environ = {'PATH_INFO':'/static'} - self.params = {} - self.cookies = {} - def copy(self): - return self - def get_response(self, app): - return app - -class DummyContext: - pass - -class DummyLock: - def acquire(self): - self.acquired = True - - def release(self): - self.released = True - -class DummyPackage: - def __init__(self, name): - self.__name__ = name - -class DummyOverrides: - def __init__(self, package): - self.package = package - self.inserted = [] - - def insert(self, path, package, prefix): - self.inserted.append((path, package, prefix)) - -class DummyUnderOverride: - def __call__(self, package, path, override_package, override_prefix, - _info=u''): - self.package = package - self.path = path - self.override_package = override_package - self.override_prefix = override_prefix - -from zope.interface import Interface -class IDummy(Interface): - pass - -class IOther(Interface): - pass - -class DummyResponse: - status = '200 OK' - headerlist = () - app_iter = () - body = '' - -class DummyLogger: - def __init__(self): - self.messages = [] - def info(self, msg): - self.messages.append(msg) - warn = info - debug = info - -class DummySecurityPolicy: - def __init__(self, permitted=True): - self.permitted = permitted - - def effective_principals(self, request): - return [] - - def permits(self, context, principals, permission): - return self.permitted - -class DummyConfigurator(object): - def __init__(self, registry=None, package=None, root_factory=None, - settings=None): - self.root_factory = root_factory - self.package = package - self.settings = settings - - def begin(self, request=None): - self.begun = True - self.request = request - - def end(self): - self.ended = True - - def load_zcml(self, filename): - self.zcml_file = filename - - def make_wsgi_app(self): - return self - - def hook_zca(self): - self.zca_hooked = True - - -class DummyAccept(object): - def __init__(self, *matches): - self.matches = list(matches) - - def best_match(self, offered): - if self.matches: - for match in self.matches: - if match in offered: - self.matches.remove(match) - return match - def __contains__(self, val): - return val in self.matches - -from zope.interface import implements -from pyramid.interfaces import IMultiView -class DummyMultiView: - implements(IMultiView) - def __init__(self): - self.views = [] - self.name = 'name' - def add(self, view, order, accept=None, phash=None): - self.views.append((view, accept, phash)) - def __call__(self, context, request): - return 'OK1' - def __permitted__(self, context, request): - """ """ - -class DummyGetSiteManager(object): - def sethook(self, hook): - self.hook = hook - def reset(self): - self.unhooked = True - -class DummyThreadLocalManager(object): - pushed = None - popped = False - def push(self, d): - self.pushed = d - def pop(self): - self.popped = True - -class IFactory(Interface): - pass - -class DummyFactory(object): - implements(IFactory) - def __call__(self): - """ """ - -class DummyEvent: - implements(IDummy) - -class DummyStaticURLInfo: - def __init__(self): - self.added = [] - - def add(self, name, spec, **kw): - self.added.append((name, spec, kw)) - -def dummy_view(request): - return 'OK' - -def dummyfactory(request): - """ """ - -class DummyHandler(object): # pragma: no cover - def __init__(self, request): - self.request = request - - def action1(self): - return 'response 1' + self.assertEqual(config.autocommit, True) + - def action2(self): - return 'response 2' diff --git a/pyramid/tests/test_events.py b/pyramid/tests/test_events.py index 0cf0cc6dd..9f655959d 100644 --- a/pyramid/tests/test_events.py +++ b/pyramid/tests/test_events.py @@ -1,4 +1,5 @@ import unittest +from pyramid import testing class NewRequestEventTests(unittest.TestCase): def _getTargetClass(self): @@ -122,8 +123,7 @@ class ContextFoundEventTests(unittest.TestCase): class TestSubscriber(unittest.TestCase): def setUp(self): registry = DummyRegistry() - from pyramid.configuration import Configurator - self.config = Configurator(registry) + self.config = testing.setUp(registry=registry) self.config.begin() def tearDown(self): diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index c70985e79..2c8892eef 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -31,10 +31,11 @@ class WGSIAppPlusViewConfigTests(unittest.TestCase): from pyramid.interfaces import IRequest from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier - from pyramid.configuration import Configurator + from pyramid.config import Configurator from pyramid.tests import test_integration config = Configurator() config.scan(test_integration) + config.commit() reg = config.registry view = reg.adapters.lookup( (IViewClassifier, IRequest, INothing), IView, name='') @@ -66,10 +67,11 @@ class TestStaticApp(unittest.TestCase): class IntegrationBase(unittest.TestCase): root_factory = None def setUp(self): - from pyramid.configuration import Configurator + from pyramid.config import Configurator config = Configurator(root_factory=self.root_factory) config.begin() config.load_zcml(self.config) + config.commit() app = config.make_wsgi_app() from webtest import TestApp self.testapp = TestApp(app) @@ -239,6 +241,32 @@ class TestExceptionViewsApp(IntegrationBase): res = self.testapp.get('/route_raise_exception4', status=200) self.failUnless('whoa' in res.body) +class ImperativeIncludeConfigurationTest(unittest.TestCase): + def setUp(self): + from pyramid.config import Configurator + config = Configurator() + from pyramid.tests.includeapp1.root import configure + configure(config) + app = config.make_wsgi_app() + from webtest import TestApp + self.testapp = TestApp(app) + self.config = config + + def tearDown(self): + self.config.end() + + def test_root(self): + res = self.testapp.get('/', status=200) + self.failUnless('root' in res.body) + + def test_two(self): + res = self.testapp.get('/two', status=200) + self.failUnless('two' in res.body) + + def test_three(self): + res = self.testapp.get('/three', status=200) + self.failUnless('three' in res.body) + class DummyContext(object): pass diff --git a/pyramid/tests/test_mako_templating.py b/pyramid/tests/test_mako_templating.py index 506dc18cd..4d1c49863 100644 --- a/pyramid/tests/test_mako_templating.py +++ b/pyramid/tests/test_mako_templating.py @@ -1,11 +1,11 @@ ## come on python gimme some of that sweet, sweet -*- coding: utf-8 -*- import unittest +from pyramid import testing class Base(object): def setUp(self): - from pyramid.configuration import Configurator - self.config = Configurator() + self.config = testing.setUp() self.config.begin() import os here = os.path.abspath(os.path.dirname(__file__)) @@ -290,8 +290,7 @@ class MakoLookupTemplateRendererTests(Base, unittest.TestCase): class TestIntegration(unittest.TestCase): def setUp(self): import pyramid.mako_templating - from pyramid.configuration import Configurator - self.config = Configurator() + self.config = testing.setUp() self.config.begin() self.config.add_settings({'mako.directories': 'pyramid.tests:fixtures'}) diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py index ab985694b..7c6b9f2da 100644 --- a/pyramid/tests/test_request.py +++ b/pyramid/tests/test_request.py @@ -1,9 +1,9 @@ import unittest +from pyramid import testing class TestRequest(unittest.TestCase): def setUp(self): - from pyramid.configuration import Configurator - self.config = Configurator() + self.config = testing.setUp() self.config.begin() def tearDown(self): diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py index fc2d87630..0ed3d79cf 100644 --- a/pyramid/tests/test_router.py +++ b/pyramid/tests/test_router.py @@ -965,6 +965,79 @@ class TestRouter(unittest.TestCase): start_response = DummyStartResponse() self.assertRaises(RuntimeError, router, environ, start_response) +class TestMakeApp(unittest.TestCase): + def setUp(self): + from zope.deprecation import __show__ + __show__.off() + testing.setUp() + + def tearDown(self): + from zope.deprecation import __show__ + __show__.on() + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from pyramid.router import make_app + return make_app(*arg, **kw) + + def test_it(self): + settings = {'a':1} + rootfactory = object() + app = self._callFUT(rootfactory, settings=settings, + Configurator=DummyConfigurator) + self.assertEqual(app.root_factory, rootfactory) + self.assertEqual(app.settings, settings) + self.assertEqual(app.zcml_file, 'configure.zcml') + self.assertEqual(app.zca_hooked, True) + + def test_it_options_means_settings(self): + settings = {'a':1} + rootfactory = object() + app = self._callFUT(rootfactory, options=settings, + Configurator=DummyConfigurator) + self.assertEqual(app.root_factory, rootfactory) + self.assertEqual(app.settings, settings) + self.assertEqual(app.zcml_file, 'configure.zcml') + + def test_it_with_package(self): + package = object() + rootfactory = object() + app = self._callFUT(rootfactory, package=package, + Configurator=DummyConfigurator) + self.assertEqual(app.package, package) + + def test_it_with_custom_configure_zcml(self): + rootfactory = object() + settings = {'configure_zcml':'2.zcml'} + app = self._callFUT(rootfactory, filename='1.zcml', settings=settings, + Configurator=DummyConfigurator) + self.assertEqual(app.zcml_file, '2.zcml') + +class DummyConfigurator(object): + def __init__(self, registry=None, package=None, root_factory=None, + settings=None, autocommit=True): + self.root_factory = root_factory + self.package = package + self.settings = settings + self.autocommit = autocommit + + def begin(self, request=None): + self.begun = True + self.request = request + + def end(self): + self.ended = True + + def load_zcml(self, filename): + self.zcml_file = filename + + def make_wsgi_app(self): + return self + + def hook_zca(self): + self.zca_hooked = True + + class DummyContext: pass diff --git a/pyramid/tests/test_settings.py b/pyramid/tests/test_settings.py index 49c1e928f..cf8fd1119 100644 --- a/pyramid/tests/test_settings.py +++ b/pyramid/tests/test_settings.py @@ -1,4 +1,5 @@ import unittest +from pyramid import testing class TestSettings(unittest.TestCase): def _getTargetClass(self): @@ -179,10 +180,9 @@ class TestSettings(unittest.TestCase): class TestGetSettings(unittest.TestCase): def setUp(self): - from pyramid.configuration import Configurator from pyramid.registry import Registry registry = Registry('testing') - self.config = Configurator(registry=registry) + self.config = testing.setUp(registry=registry) self.config.begin() from zope.deprecation import __show__ __show__.off() diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py index 81350504c..d92e5ddac 100644 --- a/pyramid/tests/test_static.py +++ b/pyramid/tests/test_static.py @@ -312,7 +312,6 @@ class TestStaticURLInfo(unittest.TestCase): expected = [('view/', 'anotherpackage:path/', False)] self.assertEqual(inst.registrations, expected) self.assertEqual(config.arg, ('view/', 'view/*subpath')) - self.assertEqual(config.kw['_info'], None) self.assertEqual(config.kw['view_for'], self._getTargetClass()) self.assertEqual(config.kw['factory'](), inst) self.assertEqual(config.kw['view_permission'], diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py index f05e7fc01..40513be81 100644 --- a/pyramid/tests/test_testing.py +++ b/pyramid/tests/test_testing.py @@ -543,9 +543,10 @@ class Test_setUp(unittest.TestCase): manager.clear() def test_it_with_registry(self): + from pyramid.registry import Registry from zope.component import getSiteManager from pyramid.threadlocal import manager - registry = object() + registry = Registry() try: self._callFUT(registry=registry) current = manager.get() @@ -569,7 +570,8 @@ class Test_setUp(unittest.TestCase): def test_it_with_hook_zca_false(self): from zope.component import getSiteManager from pyramid.threadlocal import manager - registry = object() + from pyramid.registry import Registry + registry = Registry() try: self._callFUT(registry=registry, hook_zca=False) sm = getSiteManager() diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py index 2929f888f..47aab948a 100644 --- a/pyramid/tests/test_util.py +++ b/pyramid/tests/test_util.py @@ -154,7 +154,7 @@ class TestDottedNameResolver(unittest.TestCase): self.assertEqual(typ.package_name, 'pyramid.tests') def test_ctor_string_irresolveable(self): - from pyramid.configuration import ConfigurationError + from pyramid.config import ConfigurationError self.assertRaises(ConfigurationError, self._makeOne, 'cant.be.found') def test_ctor_module(self): diff --git a/pyramid/tests/test_zcml.py b/pyramid/tests/test_zcml.py index 905a53287..8bc7c3eac 100644 --- a/pyramid/tests/test_zcml.py +++ b/pyramid/tests/test_zcml.py @@ -11,7 +11,8 @@ from zope.interface import implements class TestViewDirective(unittest.TestCase): def setUp(self): - testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() @@ -21,14 +22,12 @@ class TestViewDirective(unittest.TestCase): return view(*arg, **kw) def test_with_dotted_renderer(self): - from pyramid.threadlocal import get_current_registry from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IRendererFactory from pyramid.interfaces import IRequest - context = DummyContext() - reg = get_current_registry() - context.registry = reg + reg = self.config.registry + context = self.config._ctx def factory(path): def foo(*arg): return 'OK' @@ -37,7 +36,7 @@ class TestViewDirective(unittest.TestCase): view = lambda *arg: None self._callFUT(context, 'repoze.view', IDummy, view=view, renderer='foo/template.pt') - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) discrim = ('view', IDummy, '', None, IView, None, None, None, None, None, False, None, None, None) @@ -49,13 +48,11 @@ class TestViewDirective(unittest.TestCase): self.assertEqual(regview(None, None).body, 'OK') def test_with_custom_predicates(self): - from pyramid.threadlocal import get_current_registry from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IRequest - context = DummyContext() - reg = get_current_registry() - context.registry = reg + reg = self.config.registry + context = self.config._ctx view = lambda *arg: 'OK' def pred1(context, request): return True @@ -64,7 +61,7 @@ class TestViewDirective(unittest.TestCase): preds = (pred1, pred2) self._callFUT(context, 'repoze.view', IDummy, view=view, custom_predicates=preds) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) discrim = ('view', IDummy, '', None, IView, None, None, None, None, None, False, None, None, None) @@ -77,19 +74,17 @@ class TestViewDirective(unittest.TestCase): self.assertEqual(regview(None, None), 'OK') def test_context_trumps_for(self): - from pyramid.threadlocal import get_current_registry from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IRequest - context = DummyContext() - reg = get_current_registry() - context.registry = reg + reg = self.config.registry + context = self.config._ctx view = lambda *arg: 'OK' class Foo: pass self._callFUT(context, 'repoze.view', for_=Foo, view=view, context=IDummy) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) discrim = ('view', IDummy, '', None, IView, None, None, None, None, None, False, None, None, None) @@ -101,18 +96,16 @@ class TestViewDirective(unittest.TestCase): self.assertEqual(regview(None, None), 'OK') def test_with_for(self): - from pyramid.threadlocal import get_current_registry from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IRequest - context = DummyContext() - reg = get_current_registry() - context.registry = reg + reg = self.config.registry + context = self.config._ctx view = lambda *arg: 'OK' class Foo: pass self._callFUT(context, 'repoze.view', for_=IDummy, view=view) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) discrim = ('view', IDummy, '', None, IView, None, None, None, None, None, False, None, None, None) @@ -125,11 +118,11 @@ class TestViewDirective(unittest.TestCase): class TestNotFoundDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, context, view, **kw): from pyramid.zcml import notfound @@ -143,12 +136,11 @@ class TestNotFoundDirective(unittest.TestCase): from pyramid.exceptions import NotFound reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx def view(request): return 'OK' self._callFUT(context, view) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) discrim = ('view', NotFound, '', None, IView, None, None, None, None, @@ -171,18 +163,18 @@ class TestNotFoundDirective(unittest.TestCase): from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.exceptions import NotFound - from pyramid.configuration import Configurator + from pyramid.config import Configurator reg = self.config.registry - context = DummyContext() - context.registry = reg config = Configurator(reg) def dummy_renderer_factory(*arg, **kw): return lambda *arg, **kw: 'OK' config.add_renderer('.pt', dummy_renderer_factory) + config.commit() def view(request): return {} + context = self.config._ctx self._callFUT(context, view, renderer='fake.pt') - actions = context.actions + actions = extract_actions(context.actions) regadapt = actions[0] register = regadapt['callable'] register() @@ -195,11 +187,11 @@ class TestNotFoundDirective(unittest.TestCase): class TestForbiddenDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, context, view, **kw): from pyramid.zcml import forbidden @@ -212,12 +204,11 @@ class TestForbiddenDirective(unittest.TestCase): from pyramid.interfaces import IViewClassifier from pyramid.exceptions import Forbidden reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx def view(request): return 'OK' self._callFUT(context, view) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) @@ -241,18 +232,18 @@ class TestForbiddenDirective(unittest.TestCase): from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.exceptions import Forbidden - from pyramid.configuration import Configurator - context = DummyContext() + from pyramid.config import Configurator reg = self.config.registry - context.registry = reg config = Configurator(reg) def dummy_renderer_factory(*arg, **kw): return lambda *arg, **kw: 'OK' config.add_renderer('.pt', dummy_renderer_factory) + config.commit() def view(request): return {} + context = self.config._ctx self._callFUT(context, view, renderer='fake.pt') - actions = context.actions + actions = extract_actions(context.actions) regadapt = actions[0] register = regadapt['callable'] register() @@ -265,11 +256,11 @@ class TestForbiddenDirective(unittest.TestCase): class TestRepozeWho1AuthenticationPolicyDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, context, **kw): from pyramid.zcml import repozewho1authenticationpolicy @@ -278,10 +269,9 @@ class TestRepozeWho1AuthenticationPolicyDirective(unittest.TestCase): def test_it_defaults(self): reg = self.config.registry from pyramid.interfaces import IAuthenticationPolicy - context = DummyContext() - context.registry = reg + context = self.config._ctx self._callFUT(context) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) regadapt = actions[0] self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) @@ -294,12 +284,11 @@ class TestRepozeWho1AuthenticationPolicyDirective(unittest.TestCase): def test_it(self): reg = self.config.registry from pyramid.interfaces import IAuthenticationPolicy - context = DummyContext() - context.registry = reg + context = self.config._ctx def callback(identity, request): """ """ self._callFUT(context, identifier_name='something', callback=callback) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) regadapt = actions[0] self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) @@ -311,11 +300,11 @@ class TestRepozeWho1AuthenticationPolicyDirective(unittest.TestCase): class TestRemoteUserAuthenticationPolicyDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, context, **kw): from pyramid.zcml import remoteuserauthenticationpolicy @@ -324,12 +313,11 @@ class TestRemoteUserAuthenticationPolicyDirective(unittest.TestCase): def test_defaults(self): from pyramid.interfaces import IAuthenticationPolicy reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx def callback(identity, request): """ """ self._callFUT(context) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) regadapt = actions[0] self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) @@ -342,12 +330,11 @@ class TestRemoteUserAuthenticationPolicyDirective(unittest.TestCase): def test_it(self): from pyramid.interfaces import IAuthenticationPolicy reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx def callback(identity, request): """ """ self._callFUT(context, environ_key='BLAH', callback=callback) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) regadapt = actions[0] self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) @@ -359,11 +346,11 @@ class TestRemoteUserAuthenticationPolicyDirective(unittest.TestCase): class TestAuthTktAuthenticationPolicyDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, context, secret, **kw): from pyramid.zcml import authtktauthenticationpolicy @@ -372,10 +359,9 @@ class TestAuthTktAuthenticationPolicyDirective(unittest.TestCase): def test_it_defaults(self): from pyramid.interfaces import IAuthenticationPolicy reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx self._callFUT(context, 'sosecret') - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) regadapt = actions[0] self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) @@ -388,15 +374,14 @@ class TestAuthTktAuthenticationPolicyDirective(unittest.TestCase): def test_it_noconfigerror(self): from pyramid.interfaces import IAuthenticationPolicy reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx def callback(identity, request): """ """ self._callFUT(context, 'sosecret', callback=callback, cookie_name='auth_tkt', secure=True, include_ip=True, timeout=100, reissue_time=60, http_only=True, path="/sub/") - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) regadapt = actions[0] self.assertEqual(regadapt['discriminator'], IAuthenticationPolicy) @@ -410,7 +395,7 @@ class TestAuthTktAuthenticationPolicyDirective(unittest.TestCase): def test_it_configerror(self): from pyramid.exceptions import ConfigurationError - context = DummyContext() + context = self.config._ctx def callback(identity, request): """ """ self.assertRaises(ConfigurationError, @@ -423,11 +408,11 @@ class TestAuthTktAuthenticationPolicyDirective(unittest.TestCase): class TestACLAuthorizationPolicyDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, context, **kw): from pyramid.zcml import aclauthorizationpolicy @@ -437,12 +422,11 @@ class TestACLAuthorizationPolicyDirective(unittest.TestCase): from pyramid.authorization import ACLAuthorizationPolicy from pyramid.interfaces import IAuthorizationPolicy reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx def callback(identity, request): """ """ self._callFUT(context) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) regadapt = actions[0] self.assertEqual(regadapt['discriminator'], IAuthorizationPolicy) @@ -453,11 +437,11 @@ class TestACLAuthorizationPolicyDirective(unittest.TestCase): class TestRouteDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, *arg, **kw): from pyramid.zcml import route @@ -480,26 +464,27 @@ class TestRouteDirective(unittest.TestCase): from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IRouteRequest - context = DummyContext() reg = self.config.registry - context.registry = reg + context = self.config._ctx view = lambda *arg: 'OK' self._callFUT(context, 'name', 'pattern', view=view) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 2) - route_action = actions[0] - route_action['callable']() + view_action = actions[0] + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', None, '', None, IView, None, None, None, 'name', + None, False, None, None, None) + self.assertEqual(view_discriminator, discrim) + view_action['callable'](*view_action['args'], **view_action['kw']) + + route_action = actions[1] route_discriminator = route_action['discriminator'] self.assertEqual(route_discriminator, ('route', 'name', False, None, None, None, None,None)) self._assertRoute('name', 'pattern') - view_action = actions[1] - request_type = reg.getUtility(IRouteRequest, 'name') - view_discriminator = view_action['discriminator'] - discrim = ('view', None, '', None, IView, 'name', None) - self.assertEqual(view_discriminator, discrim) wrapped = reg.adapters.lookup( (IViewClassifier, request_type, Interface), IView, name='') self.failUnless(wrapped) @@ -508,27 +493,28 @@ class TestRouteDirective(unittest.TestCase): from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IRouteRequest - context = DummyContext() reg = self.config.registry - context.registry = reg + context = self.config._ctx view = lambda *arg: 'OK' self._callFUT(context, 'name', 'pattern', view=view, view_context=IDummy) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 2) - route_action = actions[0] - route_action['callable']() + view_action = actions[0] + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', IDummy, '', None, IView, None, None, None, 'name', + None, False, None, None, None) + self.assertEqual(view_discriminator, discrim) + view_action['callable'](*view_action['args'], **view_action['kw']) + + route_action = actions[1] route_discriminator = route_action['discriminator'] self.assertEqual(route_discriminator, ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'pattern') - view_action = actions[1] - request_type = reg.getUtility(IRouteRequest, 'name') - view_discriminator = view_action['discriminator'] - discrim = ('view', IDummy, '', None, IView, 'name', None) - self.assertEqual(view_discriminator, discrim) + self._assertRoute('name', 'pattern') wrapped = reg.adapters.lookup( (IViewClassifier, request_type, IDummy), IView, name='') self.failUnless(wrapped) @@ -538,28 +524,29 @@ class TestRouteDirective(unittest.TestCase): from pyramid.interfaces import IViewClassifier from pyramid.interfaces import IRouteRequest reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx view = lambda *arg: 'OK' class Foo: pass self._callFUT(context, 'name', 'pattern', view=view, view_context=IDummy, view_for=Foo) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 2) - route_action = actions[0] - route_action['callable']() + view_action = actions[0] + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', IDummy, '', None, IView, None, None, None, 'name', + None, False, None, None, None) + self.assertEqual(view_discriminator, discrim) + view_action['callable'](*view_action['args'], **view_action['kw']) + + route_action = actions[1] route_discriminator = route_action['discriminator'] self.assertEqual(route_discriminator, ('route', 'name', False, None, None, None, None,None)) - self._assertRoute('name', 'pattern') - view_action = actions[1] - request_type = reg.getUtility(IRouteRequest, 'name') - view_discriminator = view_action['discriminator'] - discrim = ('view', IDummy, '', None, IView, 'name', None) - self.assertEqual(view_discriminator, discrim) + self._assertRoute('name', 'pattern') wrapped = reg.adapters.lookup( (IViewClassifier, request_type, IDummy), IView, name='') self.failUnless(wrapped) @@ -574,27 +561,28 @@ class TestRouteDirective(unittest.TestCase): def renderer(path): return lambda *arg: 'OK' reg.registerUtility(renderer, IRendererFactory, name='.pt') + context = self.config._ctx - context = DummyContext() - context.registry = reg view = lambda *arg: 'OK' self._callFUT(context, 'name', 'pattern', view=view, renderer='fixtureapp/templates/foo.pt') - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 2) - route_action = actions[0] - route_action['callable']() + view_action = actions[0] + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', None, '', None, IView, None, None, None, 'name', + None, False, None, None, None) + self.assertEqual(view_discriminator, discrim) + view_action['callable'](*view_action['args'], **view_action['kw']) + + route_action = actions[1] route_discriminator = route_action['discriminator'] self.assertEqual(route_discriminator, ('route', 'name', False, None, None, None, None,None)) self._assertRoute('name', 'pattern') - view_action = actions[1] - request_type = reg.getUtility(IRouteRequest, 'name') - view_discriminator = view_action['discriminator'] - discrim = ('view', None, '', None, IView, 'name', None) - self.assertEqual(view_discriminator, discrim) wrapped = reg.adapters.lookup( (IViewClassifier, request_type, Interface), IView, name='') self.failUnless(wrapped) @@ -607,15 +595,14 @@ class TestRouteDirective(unittest.TestCase): def pred2(context, request): pass preds = tuple(sorted([pred1, pred2])) - context = DummyContext() - context.registry = self.config.registry + context = self.config._ctx + self._callFUT(context, 'name', 'pattern', custom_predicates=(pred1, pred2)) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) route_action = actions[0] - route_action['callable']() route_discriminator = route_action['discriminator'] self.assertEqual( route_discriminator, @@ -623,28 +610,24 @@ class TestRouteDirective(unittest.TestCase): self._assertRoute('name', 'pattern', 2) def test_with_path_argument_no_pattern(self): - context = DummyContext() - context.registry = self.config.registry + context = self.config._ctx self._callFUT(context, 'name', path='pattern') - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) route_action = actions[0] - route_action['callable']() route_discriminator = route_action['discriminator'] self.assertEqual(route_discriminator, ('route', 'name', False, None, None, None, None,None)) self._assertRoute('name', 'pattern') def test_with_path_argument_and_pattern(self): - context = DummyContext() - context.registry = self.config.registry + context = self.config._ctx self._callFUT(context, 'name', pattern='pattern', path='path') - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) route_action = actions[0] - route_action['callable']() route_discriminator = route_action['discriminator'] self.assertEqual(route_discriminator, ('route', 'name', False, None, None, None, None,None)) @@ -653,17 +636,96 @@ class TestRouteDirective(unittest.TestCase): def test_with_neither_path_nor_pattern(self): from pyramid.exceptions import ConfigurationError - context = DummyContext() - context.registry = self.config.registry + context = self.config._ctx self.assertRaises(ConfigurationError, self._callFUT, context, 'name') +class TestHandlerDirective(unittest.TestCase): + def setUp(self): + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, *arg, **kw): + from pyramid.zcml import handler + return handler(*arg, **kw) + + def _assertRoute(self, name, pattern, num_predicates=0): + from pyramid.interfaces import IRoutesMapper + reg = self.config.registry + mapper = reg.getUtility(IRoutesMapper) + routes = mapper.get_routes() + route = routes[0] + self.assertEqual(len(routes), 1) + self.assertEqual(route.name, name) + self.assertEqual(route.pattern, pattern) + self.assertEqual(len(routes[0].predicates), num_predicates) + return route + + def test_it(self): + from pyramid.view import action + from zope.interface import Interface + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + from pyramid.interfaces import IRouteRequest + reg = self.config.registry + context = self.config._ctx + class Handler(object): # pragma: no cover + def __init__(self, request): + self.request = request + action(renderer='json') + def one(self): + return 'OK' + action(renderer='json') + def two(self): + return 'OK' + self._callFUT(context, 'name', '/:action', Handler) + actions = extract_actions(context.actions) + self.assertEqual(len(actions), 3) + + route_action = actions[0] + route_discriminator = route_action['discriminator'] + self.assertEqual(route_discriminator, + ('route', 'name', False, None, None, None, None,None)) + self._assertRoute('name', '/:action') + + view_action = actions[1] + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', None, '', None, IView, None, None, None, 'name', + 'one', False, None, None, None) + self.assertEqual(view_discriminator[:14], discrim) + view_action['callable'](*view_action['args'], **view_action['kw']) + + view_action = actions[2] + request_type = reg.getUtility(IRouteRequest, 'name') + view_discriminator = view_action['discriminator'] + discrim = ('view', None, '', None, IView, None, None, None, 'name', + 'two', False, None, None, None) + self.assertEqual(view_discriminator[:14], discrim) + view_action['callable'](*view_action['args'], **view_action['kw']) + + wrapped = reg.adapters.lookup( + (IViewClassifier, request_type, Interface), IView, name='') + self.failUnless(wrapped) + + def test_pattern_is_None(self): + from pyramid.exceptions import ConfigurationError + + context = self.config._ctx + class Handler(object): + pass + self.assertRaises(ConfigurationError, self._callFUT, + context, 'name', None, Handler) + class TestStaticDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, *arg, **kw): from pyramid.zcml import static @@ -680,26 +742,31 @@ class TestStaticDirective(unittest.TestCase): from pyramid.interfaces import IRouteRequest from pyramid.interfaces import IRoutesMapper reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx + self._callFUT(context, 'name', 'fixtures/static') - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 2) - route_action = actions[0] + view_action = actions[0] + discriminator = view_action['discriminator'] + self.assertEqual(discriminator[:3], ('view', StaticURLInfo, '')) + self.assertEqual(discriminator[4], IView) + view_action['callable'](*view_action['args'], **view_action['kw']) + + route_action = actions[1] discriminator = route_action['discriminator'] - self.assertEqual(discriminator, ('static', 'name')) - route_action['callable'](*route_action['args'], **route_action['kw']) + self.assertEqual( + discriminator, + ('route', 'name/', False, None, None, None, None, None) + ) + mapper = reg.getUtility(IRoutesMapper) routes = mapper.get_routes() self.assertEqual(len(routes), 1) self.assertEqual(routes[0].pattern, 'name/*subpath') self.assertEqual(routes[0].name, 'name/') - view_action = actions[1] - discriminator = view_action['discriminator'] - self.assertEqual(discriminator[:3], ('view', StaticURLInfo, '')) - self.assertEqual(discriminator[4], IView) iface = implementedBy(StaticURLInfo) request_type = reg.getUtility(IRouteRequest, 'name/') view = reg.adapters.lookup( @@ -718,26 +785,29 @@ class TestStaticDirective(unittest.TestCase): from pyramid.interfaces import IRouteRequest from pyramid.interfaces import IRoutesMapper reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx self._callFUT(context, 'name', 'fixtures/static', permission='aperm') - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 2) - route_action = actions[0] + view_action = actions[0] + discriminator = view_action['discriminator'] + self.assertEqual(discriminator[:3], ('view', StaticURLInfo, '')) + self.assertEqual(discriminator[4], IView) + view_action['callable'](*view_action['args'], **view_action['kw']) + + route_action = actions[1] discriminator = route_action['discriminator'] - self.assertEqual(discriminator, ('static', 'name')) - route_action['callable'](*route_action['args'], **route_action['kw']) + self.assertEqual( + discriminator, + ('route', 'name/', False, None, None, None, None, None)) mapper = reg.getUtility(IRoutesMapper) routes = mapper.get_routes() self.assertEqual(len(routes), 1) self.assertEqual(routes[0].pattern, 'name/*subpath') self.assertEqual(routes[0].name, 'name/') - view_action = actions[1] - discriminator = view_action['discriminator'] - self.assertEqual(discriminator[:3], ('view', StaticURLInfo, '')) - self.assertEqual(discriminator[4], IView) + iface = implementedBy(StaticURLInfo) request_type = reg.getUtility(IRouteRequest, 'name/') view = reg.adapters.lookup( @@ -747,37 +817,41 @@ class TestStaticDirective(unittest.TestCase): class TestResourceDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, *arg, **kw): from pyramid.zcml import resource return resource(*arg, **kw) def test_it(self): - from pyramid.configuration import Configurator - context = DummyContext() - context.registry = self.config.registry - self._callFUT(context, 'a', 'b') - actions = context.actions + import pyramid.tests + context = self.config._ctx + L = [] + def dummy_override(*arg): + L.append(arg) + self._callFUT(context, 'pyramid.tests:fixtures/', + 'pyramid.tests:fixtureapp/', _override=dummy_override) + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) action = actions[0] - self.assertEqual(action['callable'].im_func, - Configurator.override_resource.im_func) self.assertEqual(action['discriminator'], None) - self.assertEqual(action['args'], ('a', 'b', None)) + action['callable']() + self.assertEqual( + L, + [(pyramid.tests, 'fixtures/', pyramid.tests, 'fixtureapp/')]) class TestRendererDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, *arg, **kw): from pyramid.zcml import renderer @@ -786,11 +860,10 @@ class TestRendererDirective(unittest.TestCase): def test_it(self): from pyramid.interfaces import IRendererFactory reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx renderer = lambda *arg, **kw: None self._callFUT(context, renderer, 'r') - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) action = actions[0] self.assertEqual(action['discriminator'], (IRendererFactory, 'r')) @@ -805,7 +878,7 @@ class TestZCMLConfigure(unittest.TestCase): def setUp(self): from zope.deprecation import __show__ __show__.off() - testing.setUp() + testing.setUp(autocommit=False) self.tempdir = None import sys import os @@ -848,36 +921,35 @@ class TestZCMLConfigure(unittest.TestCase): class TestZCMLScanDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, context, package): from pyramid.zcml import scan return scan(context, package) def test_it(self): - from pyramid.configuration import Configurator dummy_module = DummyModule() - context = DummyContext() - context.registry = self.config.registry + def foo(): pass + def bar(scanner, name, ob): + dummy_module.scanned = True + foo.__venusian_callbacks__ = {'pyramid':[bar]} + dummy_module.foo = foo + + context = self.config._ctx self._callFUT(context, dummy_module) - actions = context.actions - self.assertEqual(len(actions), 1) - action = actions[0] - self.assertEqual(action['callable'].im_func, Configurator.scan.im_func) - self.assertEqual(action['discriminator'], None) - self.assertEqual(action['args'], (dummy_module, None, None)) + self.assertEqual(dummy_module.scanned, True) class TestAdapterDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, *arg, **kw): from pyramid.zcml import adapter @@ -903,8 +975,9 @@ class TestAdapterDirective(unittest.TestCase): factory = DummyFactory() factory.__component_adapts__ = (IDummy,) self._callFUT(context, [factory], provides=IFactory, for_=None) - self.assertEqual(len(context.actions), 1) - regadapt = context.actions[0] + actions = extract_actions(context.actions) + self.assertEqual(len(actions), 1) + regadapt = actions[0] self.assertEqual(regadapt['discriminator'], ('adapter', (IDummy,), IFactory, '')) self.assertEqual(regadapt['callable'].im_func, @@ -923,7 +996,7 @@ class TestAdapterDirective(unittest.TestCase): context = DummyContext() context.registry = self.config.registry self._callFUT(context, [DummyFactory], for_=(IDummy,)) - regadapt = context.actions[0] + regadapt = extract_actions(context.actions)[0] self.assertEqual(regadapt['discriminator'], ('adapter', (IDummy,), IFactory, '')) self.assertEqual(regadapt['callable'].im_func, @@ -955,7 +1028,7 @@ class TestAdapterDirective(unittest.TestCase): [factory, factory], provides=IFactory, for_=(IDummy,)) - regadapt = context.actions[0] + regadapt = extract_actions(context.actions)[0] self.assertEqual(regadapt['discriminator'], ('adapter', (IDummy,), IFactory, '')) self.assertEqual(regadapt['callable'].im_func, @@ -964,182 +1037,184 @@ class TestAdapterDirective(unittest.TestCase): class TestSubscriberDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, *arg, **kw): from pyramid.zcml import subscriber return subscriber(*arg, **kw) def test_no_factory_no_handler(self): - context = DummyContext() + context = self.config._ctx self.assertRaises(TypeError, self._callFUT, context, for_=None, factory=None, handler=None, provides=None) def test_handler_with_provides(self): - context = DummyContext() + context = self.config._ctx self.assertRaises(TypeError, self._callFUT, context, for_=None, factory=None, handler=1, provides=1) def test_handler_and_factory(self): - context = DummyContext() + context = self.config._ctx self.assertRaises(TypeError, self._callFUT, context, for_=None, factory=1, handler=1, provides=None) def test_no_provides_with_factory(self): - context = DummyContext() + context = self.config._ctx self.assertRaises(TypeError, self._callFUT, context, for_=None, factory=1, handler=None, provides=None) def test_adapted_by_as_for_is_None(self): - context = DummyContext() + context = self.config._ctx factory = DummyFactory() factory.__component_adapts__ = None self.assertRaises(TypeError, self._callFUT, context, for_=None, factory=factory, handler=None, provides=IFactory) def test_register_with_factory(self): - from pyramid.registry import Registry - context = DummyContext() - context.registry = self.config.registry + context = self.config._ctx factory = DummyFactory() self._callFUT(context, for_=(IDummy,), factory=factory, handler=None, provides=IFactory) - self.assertEqual(len(context.actions), 1) - subadapt = context.actions[0] + actions = extract_actions(context.actions) + self.assertEqual(len(actions), 1) + subadapt = actions[0] self.assertEqual(subadapt['discriminator'], None) - self.assertEqual(subadapt['callable'].im_func, - Registry.registerSubscriptionAdapter.im_func) - self.assertEqual(subadapt['args'], - (factory, (IDummy,), IFactory, None, None) ) + subadapt['callable'](*subadapt['args'], **subadapt['kw']) + registrations = self.config.registry._subscription_registrations + self.assertEqual(len(registrations), 1) + reg = registrations[0] + self.assertEqual( + reg[:4], + ((IDummy,), IFactory, None, factory) + ) def test_register_with_handler(self): - from pyramid.configuration import Configurator - context = DummyContext() - context.registry = self.config.registry + context = self.config._ctx factory = DummyFactory() self._callFUT(context, for_=(IDummy,), factory=None, handler=factory) - self.assertEqual(len(context.actions), 1) - subadapt = context.actions[0] + actions = extract_actions(context.actions) + self.assertEqual(len(actions), 1) + subadapt = actions[0] self.assertEqual(subadapt['discriminator'], None) - self.assertEqual(subadapt['callable'].im_func, - Configurator.add_subscriber.im_func) - self.assertEqual(subadapt['args'], (factory, (IDummy,), None) ) + subadapt['callable'](*subadapt['args'], **subadapt['kw']) + registrations = self.config.registry._handler_registrations + self.assertEqual(len(registrations), 1) + self.assertEqual( + registrations[0][:3], + ((IDummy,), u'', factory) + ) class TestUtilityDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, *arg, **kw): from pyramid.zcml import utility return utility(*arg, **kw) def test_factory_and_component(self): - context = DummyContext() + context = self.config._ctx self.assertRaises(TypeError, self._callFUT, context, factory=1, component=1) def test_missing_provides(self): - context = DummyContext() + context = self.config._ctx self.assertRaises(TypeError, self._callFUT, context, provides=None) def test_provides_from_factory_implements(self): from pyramid.registry import Registry - context = DummyContext() - context.registry = self.config.registry + context = self.config._ctx self._callFUT(context, factory=DummyFactory) - self.assertEqual(len(context.actions), 1) - utility = context.actions[0] + actions = extract_actions(context.actions) + self.assertEqual(len(actions), 1) + utility = actions[0] self.assertEqual(utility['discriminator'], ('utility', IFactory, '')) self.assertEqual(utility['callable'].im_func, Registry.registerUtility.im_func) - self.assertEqual(utility['args'], (None, IFactory, '', None)) + self.assertEqual(utility['args'][:3], (None, IFactory, '')) self.assertEqual(utility['kw'], {'factory':DummyFactory}) def test_provides_from_component_provides(self): from pyramid.registry import Registry - context = DummyContext() - context.registry = self.config.registry + context = self.config._ctx component = DummyFactory() self._callFUT(context, component=component) - self.assertEqual(len(context.actions), 1) - utility = context.actions[0] + actions = extract_actions(context.actions) + self.assertEqual(len(actions), 1) + utility = actions[0] self.assertEqual(utility['discriminator'], ('utility', IFactory, '')) self.assertEqual(utility['callable'].im_func, Registry.registerUtility.im_func) - self.assertEqual(utility['args'], (component, IFactory, '', None)) + self.assertEqual(utility['args'][:3], (component, IFactory, '')) self.assertEqual(utility['kw'], {}) class TestTranslationDirDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, *arg, **kw): from pyramid.zcml import translationdir return translationdir(*arg, **kw) def test_it(self): - from pyramid.configuration import Configurator - context = DummyContext() - context.registry = self.config.registry + import os + here = os.path.dirname(__file__) + expected = os.path.join(here, 'localeapp', 'locale') + from pyramid.interfaces import ITranslationDirectories + context = self.config._ctx tdir = 'pyramid.tests.localeapp:locale' self._callFUT(context, tdir) - actions = context.actions - self.assertEqual(len(actions), 1) - action = context.actions[0] - self.assertEqual(action['discriminator'], ('tdir', tdir)) - self.assertEqual(action['callable'].im_func, - Configurator.add_translation_dirs.im_func) - self.assertEqual(action['args'], (tdir,)) - action['callable'](*action['args']) # doesn't blow up + util = self.config.registry.getUtility(ITranslationDirectories) + self.assertEqual(util, [expected]) class TestLocaleNegotiatorDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() - self.config = None def _callFUT(self, *arg, **kw): from pyramid.zcml import localenegotiator return localenegotiator(*arg, **kw) def test_it(self): - from pyramid.configuration import Configurator - context = DummyContext() - context.registry = self.config.registry + from pyramid.interfaces import ILocaleNegotiator + context = self.config._ctx dummy_negotiator = object() self._callFUT(context, dummy_negotiator) - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) - action = context.actions[0] - self.assertEqual(action['discriminator'], 'lnegotiator') - self.assertEqual(action['callable'].im_func, - Configurator.set_locale_negotiator.im_func) - self.assertEqual(action['args'], (dummy_negotiator,)) - action['callable'](*action['args']) # doesn't blow up + action = actions[0] + self.assertEqual(action['discriminator'], ILocaleNegotiator) + callback = action['callable'] + callback() + self.assertEqual(self.config.registry.getUtility(ILocaleNegotiator), + dummy_negotiator) class TestDefaultPermissionDirective(unittest.TestCase): def setUp(self): - self.config = testing.setUp() + self.config = testing.setUp(autocommit=False) + self.config._ctx = self.config._make_context() def tearDown(self): testing.tearDown() @@ -1151,10 +1226,9 @@ class TestDefaultPermissionDirective(unittest.TestCase): def test_it(self): from pyramid.interfaces import IDefaultPermission reg = self.config.registry - context = DummyContext() - context.registry = reg + context = self.config._ctx self._callFUT(context, 'view') - actions = context.actions + actions = extract_actions(context.actions) self.assertEqual(len(actions), 1) regadapt = actions[0] self.assertEqual(regadapt['discriminator'], IDefaultPermission) @@ -1163,7 +1237,7 @@ class TestDefaultPermissionDirective(unittest.TestCase): class TestLoadZCML(unittest.TestCase): def setUp(self): - testing.setUp() + testing.setUp(autocommit=False) def tearDown(self): testing.tearDown() @@ -1257,12 +1331,7 @@ class DummyContext: self.package = None def action(self, discriminator, callable=None, args=(), kw={}, order=0): - self.actions.append( - {'discriminator':discriminator, - 'callable':callable, - 'args':args, - 'kw':kw} - ) + self.actions.append((discriminator, callable, args, kw, order)) def path(self, path): return path @@ -1291,4 +1360,19 @@ class DummyPackage(object): def __init__(self, name): self.__name__ = name self.__file__ = '/__init__.py' + +def extract_actions(native): + from zope.configuration.config import expand_action + L = [] + for action in native: + (discriminator, callable, args, kw, includepath, info, order + ) = expand_action(*action) + d = {} + d['discriminator'] = discriminator + d['callable'] = callable + d['args'] = args + d['kw'] = kw + d['order'] = order + L.append(d) + return L diff --git a/pyramid/url.py b/pyramid/url.py index 2e73e9cf5..79740821e 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -301,7 +301,7 @@ def static_url(path, request, **kw): """ Generates a fully qualified URL for a static :term:`resource`. The resource must live within a location defined via the - :meth:`pyramid.configuration.Configurator.add_static_view` + :meth:`pyramid.config.Configurator.add_static_view` :term:`configuration declaration` or the ``<static>`` ZCML directive (see :ref:`static_resources_section`). diff --git a/pyramid/util.py b/pyramid/util.py index 84a23bb23..faab2c54c 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -41,7 +41,7 @@ class DottedNameResolver(object): resolver will only be able to resolve fully qualified (not relative) names. Any attempt to resolve a relative name when the ``package`` is ``None`` will result in an - :exc:`pyramid.configuration.ConfigurationError` exception. + :exc:`pyramid.config.ConfigurationError` exception. If a *module* or *module name* (as opposed to a package or package name) is supplied as ``package``, its containing package is diff --git a/pyramid/view.py b/pyramid/view.py index 2ac51406e..d18d0dba3 100644 --- a/pyramid/view.py +++ b/pyramid/view.py @@ -155,7 +155,7 @@ class view_config(object): return 'OK' Might replace the following call to the - :meth:`pyramid.configuration.Configurator.add_view` method:: + :meth:`pyramid.config.Configurator.add_view` method:: import views import models @@ -371,7 +371,7 @@ class view_config(object): ``scan`` directive. Or, if you don't use ZCML, use the - :meth:`pyramid.configuration.Configurator.scan` method:: + :meth:`pyramid.config.Configurator.scan` method:: config.scan() """ @@ -505,7 +505,7 @@ If you use :term:`ZCML`, add the following to your application's view="pyramid.view.append_slash_notfound_view"/> Or use the -:meth:`pyramid.configuration.Configurator.add_view` +:meth:`pyramid.config.Configurator.add_view` method if you don't use ZCML:: from pyramid.exceptions import NotFound diff --git a/pyramid/wsgi.py b/pyramid/wsgi.py index 89fd120cb..69a250534 100644 --- a/pyramid/wsgi.py +++ b/pyramid/wsgi.py @@ -25,7 +25,7 @@ def wsgiapp(wrapped): /> Or the following call to - :meth:`pyramid.configuration.Configurator.add_view`:: + :meth:`pyramid.config.Configurator.add_view`:: from views import hello_world config.add_view(hello_world, name='hello_world.txt') @@ -64,7 +64,7 @@ def wsgiapp2(wrapped): /> Or the following call to - :meth:`pyramid.configuration.Configurator.add_view`:: + :meth:`pyramid.config.Configurator.add_view`:: from views import hello_world config.add_view(hello_world, name='hello_world.txt') diff --git a/pyramid/zcml.py b/pyramid/zcml.py index 3104ebac0..db314efa9 100644 --- a/pyramid/zcml.py +++ b/pyramid/zcml.py @@ -1,7 +1,5 @@ import os -from zope.configuration import xmlconfig -from zope.configuration.config import ConfigurationMachine from zope.configuration.fields import GlobalInterface from zope.configuration.fields import GlobalObject from zope.configuration.fields import Tokens @@ -17,24 +15,13 @@ from zope.schema import Bool from zope.schema import Int from zope.schema import TextLine -from pyramid.interfaces import IAuthenticationPolicy -from pyramid.interfaces import IAuthorizationPolicy -from pyramid.interfaces import IDefaultPermission -from pyramid.interfaces import IRendererFactory -from pyramid.interfaces import IRouteRequest -from pyramid.interfaces import IView - from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authentication import RemoteUserAuthenticationPolicy from pyramid.authentication import RepozeWho1AuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy -from pyramid.configuration import Configurator +from pyramid.config import Configurator from pyramid.exceptions import ConfigurationError -from pyramid.exceptions import NotFound -from pyramid.exceptions import Forbidden -from pyramid.request import route_request_iface from pyramid.resource import resource_spec_from_abspath -from pyramid.static import StaticURLInfo from pyramid.threadlocal import get_current_registry ###################### directives ########################## @@ -174,49 +161,28 @@ def view( cacheable=True, # not used, here for b/w compat < 0.8 ): - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - if renderer is not None: package = getattr(_context, 'package', None) renderer = {'name':renderer, 'package':package} context = context or for_ - def register(): - config = Configurator(reg, package=_context.package) - config.add_view( - permission=permission, context=context, view=view, name=name, - request_type=request_type, route_name=route_name, - request_method=request_method, request_param=request_param, - containment=containment, attr=attr, renderer=renderer, - wrapper=wrapper, xhr=xhr, accept=accept, header=header, - path_info=path_info, custom_predicates=custom_predicates, - _info=_context.info) - - discriminator = ['view', context, name, request_type, IView, containment, - request_param, request_method, route_name, attr, - xhr, accept, header, path_info] - - discriminator.extend(sorted(custom_predicates)) - discriminator = tuple(discriminator) - - _context.action( - discriminator = discriminator, - callable = register, - ) + config = Configurator.with_context(_context) + config.add_view( + permission=permission, context=context, view=view, name=name, + request_type=request_type, route_name=route_name, + request_method=request_method, request_param=request_param, + containment=containment, attr=attr, renderer=renderer, + wrapper=wrapper, xhr=xhr, accept=accept, header=header, + path_info=path_info, custom_predicates=custom_predicates) _view = view # for directives that take a view arg -class IRouteDirective(Interface): + +class IRouteLikeDirective(Interface): """ The interface for the ``route`` ZCML directive """ - name = TextLine(title=u'name', required=True) pattern = TextLine(title=u'pattern', required=False) - # alias for pattern - path = TextLine(title=u'path', required=False) factory = GlobalObject(title=u'context factory', required=False) view = GlobalObject(title=u'view', required=False) @@ -256,6 +222,11 @@ class IRouteDirective(Interface): ) use_global_views = Bool(title=u'use_global_views', required=False) +class IRouteDirective(IRouteLikeDirective): + name = TextLine(title=u'name', required=True) + # alias for pattern + path = TextLine(title=u'path', required=False) + def route(_context, name, pattern=None, @@ -286,11 +257,6 @@ def route(_context, # these are route predicates; if they do not match, the next route # in the routelist will be tried - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - if view_context is None: view_context = view_for or for_ @@ -303,48 +269,94 @@ def route(_context, if pattern is None: raise ConfigurationError('route directive must include a "pattern"') - def register(): - config = Configurator(reg, package=_context.package) - config.add_route( - name, + config = Configurator.with_context(_context) + config.add_route( + name, + pattern, + factory=factory, + header=header, + xhr=xhr, + accept=accept, + path_info=path_info, + request_method=request_method, + request_param=request_param, + custom_predicates=custom_predicates, + view=view, + view_context=view_context, + view_permission=view_permission, + view_renderer=view_renderer, + view_attr=view_attr, + use_global_views=use_global_views, + traverse=traverse, + ) + +class IHandlerDirective(IRouteLikeDirective): + route_name = TextLine(title=u'route_name', required=True) + handler = GlobalObject(title=u'handler', required=True) + action = TextLine(title=u"action", required=False) + +def handler(_context, + route_name, pattern, - factory=factory, - header=header, - xhr=xhr, - accept=accept, - path_info=path_info, - request_method=request_method, - request_param=request_param, - custom_predicates=custom_predicates, - view=view, - view_context=view_context, - view_permission=view_permission, - view_renderer=view_renderer, - view_attr=view_attr, - use_global_views=use_global_views, - traverse=traverse, - _info=_context.info - ) + handler, + action=None, + view=None, + view_for=None, + permission=None, + factory=None, + for_=None, + header=None, + xhr=False, + accept=None, + path_info=None, + request_method=None, + request_param=None, + custom_predicates=(), + view_permission=None, + view_attr=None, + renderer=None, + view_renderer=None, + view_context=None, + traverse=None, + use_global_views=False): + """ Handle ``handler`` ZCML directives + """ + # the strange ordering of the request kw args above is for b/w + # compatibility purposes. - discriminator = ['route', name, xhr, request_method, path_info, - request_param, header, accept] - discriminator.extend(sorted(custom_predicates)) - discriminator = tuple(discriminator) - - _context.action( - discriminator=discriminator, - callable = register, - ) + # these are route predicates; if they do not match, the next route + # in the routelist will be tried + if view_context is None: + view_context = view_for or for_ - if view: - request_iface = reg.queryUtility(IRouteRequest, name=name) - if request_iface is None: - request_iface = route_request_iface(name) - reg.registerUtility(request_iface, IRouteRequest, name=name) - _context.action( - discriminator = ( - 'view', view_context, '', None, IView, name, view_attr), - ) + view_permission = view_permission or permission + view_renderer = view_renderer or renderer + + if pattern is None: + raise ConfigurationError('handler directive must include a "pattern"') + + config = Configurator.with_context(_context) + config.add_handler( + route_name, + pattern, + handler, + action=action, + factory=factory, + header=header, + xhr=xhr, + accept=accept, + path_info=path_info, + request_method=request_method, + request_param=request_param, + custom_predicates=custom_predicates, + view=view, + view_context=view_context, + view_permission=view_permission, + view_renderer=view_renderer, + view_attr=view_attr, + use_global_views=use_global_views, + traverse=traverse, + ) class ISystemViewDirective(Interface): view = GlobalObject( @@ -374,22 +386,10 @@ def notfound(_context, renderer=None, wrapper=None): - def register(): - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config.set_notfound_view(view=view, attr=attr, renderer=renderer, - wrapper=wrapper, _info=_context.info) - - discriminator = ('view', NotFound, '', None, IView, None, None, None, - None, attr, False, None, None, None) + config = Configurator.with_context(_context) + config.set_notfound_view(view=view, attr=attr, renderer=renderer, + wrapper=wrapper) - _context.action( - discriminator = discriminator, - callable = register, - ) def forbidden(_context, view=None, @@ -397,22 +397,10 @@ def forbidden(_context, renderer=None, wrapper=None): - def register(): - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config.set_forbidden_view(view=view, attr=attr, renderer=renderer, - wrapper=wrapper, _info=_context.info) - - discriminator = ('view', Forbidden, '', None, IView, None, None, None, - None, attr, False, None, None, None) + config = Configurator.with_context(_context) + config.set_forbidden_view(view=view, attr=attr, renderer=renderer, + wrapper=wrapper) - _context.action( - discriminator = discriminator, - callable = register, - ) class IResourceDirective(Interface): """ @@ -428,19 +416,9 @@ class IResourceDirective(Interface): description=u"The spec of the resource providing the override.", required=True) -def resource(_context, to_override, override_with): - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - - config = Configurator(reg, package=_context.package) - - _context.action( - discriminator = None, - callable = config.override_resource, - args = (to_override, override_with, _context.info), - ) +def resource(_context, to_override, override_with, _override=None): + config = Configurator.with_context(_context) + config.override_resource(to_override, override_with, _override=_override) class IRepozeWho1AuthenticationPolicyDirective(Interface): identifier_name = TextLine(title=u'identitfier_name', required=False, @@ -453,13 +431,8 @@ def repozewho1authenticationpolicy(_context, identifier_name='auth_tkt', callback=callback) # authentication policies must be registered eagerly so they can # be found by the view registration machinery - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config._set_authentication_policy(policy, _info=_context.info) - _context.action(discriminator=IAuthenticationPolicy) + config = Configurator.with_context(_context) + config._set_authentication_policy(policy) class IRemoteUserAuthenticationPolicyDirective(Interface): environ_key = TextLine(title=u'environ_key', required=False, @@ -472,13 +445,8 @@ def remoteuserauthenticationpolicy(_context, environ_key='REMOTE_USER', callback=callback) # authentication policies must be registered eagerly so they can # be found by the view registration machinery - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config._set_authentication_policy(policy, _info=_context.info) - _context.action(discriminator=IAuthenticationPolicy) + config = Configurator.with_context(_context) + config._set_authentication_policy(policy) class IAuthTktAuthenticationPolicyDirective(Interface): secret = TextLine(title=u'secret', required=True) @@ -519,13 +487,8 @@ def authtktauthenticationpolicy(_context, raise ConfigurationError(str(why)) # authentication policies must be registered eagerly so they can # be found by the view registration machinery - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config._set_authentication_policy(policy, _info=_context.info) - _context.action(discriminator=IAuthenticationPolicy) + config = Configurator.with_context(_context) + config._set_authentication_policy(policy) class IACLAuthorizationPolicyDirective(Interface): pass @@ -534,13 +497,8 @@ def aclauthorizationpolicy(_context): policy = ACLAuthorizationPolicy() # authorization policies must be registered eagerly so they can be # found by the view registration machinery - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config._set_authorization_policy(policy, _info=_context.info) - _context.action(discriminator=IAuthorizationPolicy) + config = Configurator.with_context(_context) + config._set_authorization_policy(policy) class IRendererDirective(Interface): factory = GlobalObject( @@ -554,13 +512,8 @@ class IRendererDirective(Interface): def renderer(_context, factory, name=''): # renderer factories must be registered eagerly so they can be # found by the view machinery - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - config.add_renderer(name, factory, _info=_context.info) - _context.action(discriminator=(IRendererFactory, name)) + config = Configurator.with_context(_context) + config.add_renderer(name, factory) class IStaticDirective(Interface): name = TextLine( @@ -590,28 +543,9 @@ def static(_context, name, path, cache_max_age=3600, permission='__no_permission_required__'): """ Handle ``static`` ZCML directives """ - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - - _context.action( - discriminator=('static', name), - callable=config.add_static_view, - args = (name, path), - kw = {'cache_max_age':cache_max_age, - 'permission':permission, - '_info':_context.info}, - ) - - if not '/' in name: - _context.action( - discriminator = ( - 'view', StaticURLInfo, '', None, IView, None, None, None, - name, None, None, None, None, None, - ) - ) + config = Configurator.with_context(_context) + config.add_static_view(name, path, cache_max_age=cache_max_age, + permission=permission) class IScanDirective(Interface): package = GlobalObject( @@ -620,16 +554,8 @@ class IScanDirective(Interface): ) def scan(_context, package): - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - _context.action( - discriminator=None, - callable=config.scan, - args=(package, None, _context.info) - ) + config = Configurator.with_context(_context) + config.scan(package) class ITranslationDirDirective(Interface): dir = TextLine( @@ -640,18 +566,8 @@ class ITranslationDirDirective(Interface): def translationdir(_context, dir): path = path_spec(_context, dir) - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - - config = Configurator(reg, package=_context.package) - - _context.action( - discriminator = ('tdir', path), - callable=config.add_translation_dirs, - args = (dir,), - ) + config = Configurator.with_context(_context) + config.add_translation_dirs(path) class ILocaleNegotiatorDirective(Interface): negotiator = GlobalObject( @@ -661,17 +577,8 @@ class ILocaleNegotiatorDirective(Interface): ) def localenegotiator(_context, negotiator): - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) - - _context.action( - discriminator = 'lnegotiator', - callable=config.set_locale_negotiator, - args = (negotiator,) - ) + config = Configurator.with_context(_context) + config.set_locale_negotiator(negotiator) class IAdapterDirective(Interface): """ @@ -809,20 +716,12 @@ def subscriber(_context, for_=None, factory=None, handler=None, provides=None): for_ = tuple(for_) - try: - registry = _context.registry - except AttributeError: # pragma: no cover (b/c) - registry = get_current_registry() - - config = Configurator(registry=registry, package=_context.package) + config = Configurator.with_context(_context) if handler is not None: - _context.action( - discriminator = None, - callable = config.add_subscriber, - args = (handler, for_, _context.info), - ) + config.add_subscriber(handler, for_) else: + registry = _context.registry _context.action( discriminator = None, callable = registry.registerSubscriptionAdapter, @@ -904,13 +803,8 @@ def default_permission(_context, name): """ Register a default permission name """ # the default permission must be registered eagerly so it can # be found by the view registration machinery - try: - reg = _context.registry - except AttributeError: # pragma: no cover (b/c) - reg = get_current_registry() - config = Configurator(reg, package=_context.package) + config = Configurator.with_context(_context) config.set_default_permission(name) - _context.action(discriminator=IDefaultPermission) def path_spec(context, path): # we prefer registering resource specifications over absolute @@ -929,25 +823,24 @@ def zcml_configure(name, package): ZCML into the current ZCML registry. """ - context = ConfigurationMachine() - xmlconfig.registerCommonDirectives(context) - context.package = package - context.registry = get_current_registry() - xmlconfig.include(context, name, package) - context.execute_actions(clear=False) # the raison d'etre - return context.actions + registry = get_current_registry() + configurator = Configurator(registry=registry, package=package) + configurator.load_zcml(name) + actions = configurator._ctx.actions[:] + configurator.commit() + return actions file_configure = zcml_configure # backwards compat (>0.8.1) deprecated( 'zcml_configure', '(pyramid.zcml.zcml_configure is deprecated as of Pyramid 1.0. Use' - '``pyramid.configuration.Configurator.load_zcml`` instead.) ') + '``pyramid.config.Configurator.load_zcml`` instead.) ') deprecated( 'file_configure', '(pyramid.zcml.file_configure is deprecated as of Pyramid 1.0. Use' - '``pyramid.configuration.Configurator.load_zcml`` instead.) ') + '``pyramid.config.Configurator.load_zcml`` instead.) ') def _rolledUpFactory(factories): def factory(ob): |
