summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2010-07-26 00:26:10 +0000
committerChris McDonough <chrism@agendaless.com>2010-07-26 00:26:10 +0000
commit250c0218d0bd7dab6ea7e16c7051af71394f2a63 (patch)
treed57c38c27b72a483a3db9b1150d20553a93472d8
parent2eb64f7a8bc7830667c3cb924bb5c13be3859b38 (diff)
downloadpyramid-250c0218d0bd7dab6ea7e16c7051af71394f2a63.tar.gz
pyramid-250c0218d0bd7dab6ea7e16c7051af71394f2a63.tar.bz2
pyramid-250c0218d0bd7dab6ea7e16c7051af71394f2a63.zip
merge generic_rendering branch
-rw-r--r--CHANGES.txt137
-rw-r--r--TODO.txt101
-rw-r--r--docs/api.rst1
-rw-r--r--docs/api/configuration.rst10
-rw-r--r--docs/api/renderers.rst13
-rw-r--r--docs/glossary.rst5
-rw-r--r--docs/narr/hooks.rst66
-rw-r--r--docs/narr/templates.rst236
-rw-r--r--repoze/bfg/chameleon_text.py50
-rw-r--r--repoze/bfg/chameleon_zpt.py50
-rw-r--r--repoze/bfg/configuration.py273
-rw-r--r--repoze/bfg/interfaces.py9
-rw-r--r--repoze/bfg/renderers.py182
-rw-r--r--repoze/bfg/settings.py4
-rw-r--r--repoze/bfg/testing.py116
-rw-r--r--repoze/bfg/tests/test_chameleon_text.py87
-rw-r--r--repoze/bfg/tests/test_chameleon_zpt.py86
-rw-r--r--repoze/bfg/tests/test_configuration.py158
-rw-r--r--repoze/bfg/tests/test_renderers.py307
-rw-r--r--repoze/bfg/tests/test_testing.py71
20 files changed, 1382 insertions, 580 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 2520b8f5e..1dc24a83d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -42,11 +42,46 @@ Features
- A new method of the ``Configurator`` exists:
``set_request_factory``. If used, this method will set the factory
- used by the :mod:`repoze.bfg` router to create all request objects.
+ used by the ``repoze.bfg`` router to create all request objects.
- The ``Configurator`` constructor takes an additional argument:
``request_factory``. If used, this argument will set the factory
- used by the :mod:`repoze.bfg` router to create all request objects.
+ used by the ``repoze.bfg`` router to create all request objects.
+
+- The ``Configurator`` constructor takes an additional argument:
+ ``request_factory`. If used, this argument will set the factory
+ used by the ``repoze.bfg`` router to create all request objects.
+
+- A new method of the ``Configurator`` exists:
+ ``set_renderer_globals_factory``. If used, this method will set the
+ factory used by the ``repoze.bfg`` router to create renderer
+ globals.
+
+- A new method of the ``Configurator`` exists: ``get_settings``. If
+ used, this method will return the current settings object (performs
+ the same job as the ``repoze.bfg.settings.get_settings`` API).
+
+- The ``Configurator`` constructor takes an additional argument:
+ ``renderer_globals_factory`. If used, this argument will set the
+ factory used by the ``repoze.bfg`` router to create renderer
+ globals.
+
+- Add ``repoze.bfg.renderers.render``,
+ ``repoze.bfg.renderers.render_to_response`` and
+ ``repoze.bfg.renderers.get_renderer`` functions. These are
+ imperative APIs which will use the same rendering machinery used by
+ view configurations with a ``renderer=`` attribute/argument to
+ produce a rendering or renderer. Because these APIs provide a
+ central API for all rendering, they now form the preferred way to
+ perform imperative template rendering. Using functions named
+ ``render_*` from modules such as ``repoze.bfg.chameleon_zpt`` and
+ ``repoze.bfg.chameleon_text`` is now discouraged (although not
+ deprecated). The code the backing older templating-system-specific
+ APIs now calls into the newer ``repoze.bfg.renderer`` code.
+
+- The ``repoze.bfg.configuration.Configurator.testing_add_template``
+ has been renamed to ``testing_add_renderer``. A backwards
+ compatibility alias is present using the old name.
Documentation
-------------
@@ -57,6 +92,15 @@ Documentation
- The ``Hooks`` narrative chapter now contains a section about
changing the request factory.
+- The API documentation includes a new module:
+ ``repoze.bfg.renderers``.
+
+- The ``Templates`` chapter was updated; all narrative that used
+ templating-specific APIs within examples to perform rendering (such
+ as the ``repoze.bfg.chameleon_zpt.render_template_to_response``
+ method) was changed to use ``repoze.bfg.renderers.render_*``
+ functions.
+
Bug Fixes
---------
@@ -67,6 +111,34 @@ Bug Fixes
``TypeError: expected string or buffer`` exception. Now, the
predicate returns False as intended.
+Deprecations
+------------
+
+- The ``repoze.bfg.renderers.rendered_response`` function was never an
+ official API, but may have been imported by extensions in the wild.
+ It is officially deprecated in this release. Use
+ ``repoze.bfg.renderers.render_to_response`` instead.
+
+- The following APIs are *documentation* deprecated (meaning they are
+ officially deprecated in documentation but do not raise a
+ deprecation error upon their usage, and may continue to work for an
+ indefinite period of time):
+
+ In the ``repoze.bfg.chameleon_zpt`` module: ``get_renderer``,
+ ``get_template``, ``render_template``,
+ ``render_template_to_response``. The suggested alternatives are
+ documented within the docstrings of those methods (which are still
+ present in the documentation).
+
+ In the ``repoze.bfg.chameleon_text`` module: ``get_renderer``,
+ ``get_template``, ``render_template``,
+ ``render_template_to_response``. The suggested alternatives are
+ documented within the docstrings of those methods (which are still
+ present in the documentation).
+
+ In general, to perform template-related functions, one should now
+ use the various methods in the ``repoze.bfg.renderers`` module.
+
Backwards Incompatibilities
---------------------------
@@ -98,6 +170,67 @@ Backwards Incompatibilities
the logic which raised the ``NotFound`` exception in the view out
into a custom view predicate.
+- If, when you run your application's unit test suite under BFG 1.3, a
+ ``KeyError`` naming a template or a ``ValueError`` indicating that a
+ 'renderer factory' is not registered may is raised
+ (e.g. ``ValueError: No factory for renderer named '.pt' when looking
+ up karl.views:templates/snippets.pt``), you may need to perform some
+ extra setup in your test code.
+
+ The best solution is to use the
+ ``repoze.bfg.configuration.Configurator.testing_add_renderer`` (or,
+ alternately the deprecated
+ ``repoze.bfg.testing.registerTemplateRenderer`` or
+ ``registerDummyRenderer``) API within the code comprising each
+ individual unit test suite to register a "dummy" renderer for each
+ of the templates and renderers used by code under test. For
+ example::
+
+ config = Configurator()
+ config.testing_add_renderer('karl.views:templates/snippets.pt')
+
+ This will register a basic dummy renderer for this particular
+ missing template. The ``testing_add_renderer`` API actually
+ *returns* the renderer, but if you don't care about how the render
+ is used, you don't care about having a reference to it either.
+
+ A more rough way to solve the issue exists. It causes the "real"
+ template implementations to be used while the system is under test,
+ which is suboptimal, because tests will run slower, and unit tests
+ won't actually *be* unit tests, but it is easier. Always ensure you
+ call the ``setup_registry()`` method of the Configurator . Eg::
+
+ reg = MyRegistry()
+ config = Configurator(registry=reg)
+ config.setup_registry()
+
+ Calling ``setup_registry`` only has an effect if you're *passing in*
+ a ``registry`` argument to the Configurator constructor.
+ ``setup_registry`` is called by the course of normal operations
+ anyway if you do not pass in a ``registry``.
+
+ If your test suite isn't using a Configurator yet, and is still
+ using the older ``repoze.bfg.testing`` APIs name ``setUp`` or
+ ``cleanUp``, these will register the renderers on your behalf.
+
+ A variant on the symptom for this theme exists: you may already be
+ dutifully registering a dummy template or renderer for a template
+ used by the code you're testing using ``testing_register_renderer``
+ or ``registerTemplateRenderer``, but (perhaps unbeknownst to you)
+ the code under test expects to be able to use a "real" template
+ renderer implementation to retrieve or render *another* template
+ that you forgot was being rendered as a side effect of calling the
+ code you're testing. This happened to work because it found the
+ *real* template while the system was under test previously, and now
+ it cannot. The solution is the same.
+
+ It may also help reduce confusion to use a *resource specification*
+ to specify the template path in the test suite and code rather than
+ a relative path in either. A resource specification is unambiguous,
+ while a relative path needs to be relative to "here", where "here"
+ isn't always well-defined ("here" in a test suite may or may not be
+ the same as "here" in the code under test).
+
1.3a5 (2010-07-14)
==================
diff --git a/TODO.txt b/TODO.txt
index 544e6f2f4..864eb2ab5 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -15,50 +15,10 @@
- ``decorator=`` parameter to bfg_view.
-Renderer overhaul
-------------------
-
-Currently the division of responsibility between the BFG configurator
-and a BFG renderer implementation is awkward and wrong.
-
-- Renderer factories have no ability to convert a raw ``renderer=``
- path (e.g. ``templates/foo.pt```) into something internally
- meaningful. Instead, BFG mangles the string into a package-relative
- spec before it is passed to the renderer factory. This is wrong, as
- some renderers may not be interested in package-relative specs at
- all (for instance, loader-style renderers which have a hardcoded set
- of template locations). The reason, however, that BFG currently
- does it this way is that the raw renderer path alone does not
- contain enough information itself to be useful; knowledge of the
- *package* is also required for package-based renderers to make sense
- of relative renderer strings (e.g. ``templates/foo.pt`` could mean
- the ``templates/foo.pt`` file within the ``mypackage`` package).
-
- To fix this, we need to provide some way to pass the package name to
- the renderer factory as well as the renderer path. But the package
- name isn't the only thing an *arbitrary* renderer might need.
- Another renderer might need, for example, a deployment setting. So
- we'll need to identify all the crap that *might* be useful to a
- renderer factory and we'll need to pass all of it into the renderer
- factory as a garbage barge dictionary; individual renderers will
- make use of whatever they can from that garbage barge dictionary.
- Garbage barge dict item candidates: ``package`` (the "current"
- package), ``config`` (the configurator), ``package_name`` (the
- current package's ``__name__``), ``settings`` (the deployment
- settings), ``registry`` (the component registry).
-
-- A BFG renderer currently returns a *string*. It would be more
- symmetric if a renderer always returned a Response object. Then the
- calling machinery inside BFG could treat a view which happened to
- use a renderer exactly the same as a view which returns a response
- directly. Maybe. Problem: varying response attributes such as
- ``content-type``, etc only makes sense when the view callable uses a
- renderer; not when it doesn't, so there's some asymmetry there.
- Maybe we make renderers return Responses, but still keep the
- attribute-inspection stuff inside BFG, only used when we call a view
- which we know uses a renderer. We *could* always call the attribute
- inspection stuff, but it would be a slowdown in cases where views
- really do always return a Response directly.
+- Try to better explain the relationship between a renderer and a
+ template in the templates chapter and elsewhere. Scan the
+ documentation for reference to a renderer as *only* view
+ configuration (it's a larger concept now).
- The ``system`` value passed to a renderer is not extensible. It
should be extensible on a per-application basis. For example, you
@@ -71,56 +31,3 @@ and a BFG renderer implementation is awkward and wrong.
``system`` values available to it as templates renderered via a view
renderer.
-To at least partially ameliorate the above, renderer factories should
-be changed to things that have a set of interfaces something like
-this::
-
- class IRendererFactory(Interface):
- def __call__(path, info):
- "" Return an IRenderer."""
-
- class IRenderer(Interface):
- def __call__(value, system):
- """ Return a Response """
-
-A semi-pseudocode example:
-
- from webob import Response
-
- class SampleRendererFactory(object):
- def __init__(self, **config):
- self.config = config
-
- def __call__(self, path, info):
- path = do_something_to_evaluate_path_using_info(path, info)
- search_path = self.config['search_path']
- debug = self.config['debug']
- def renderer(value, system):
- string = do_rendering(search_path, debug, path, value, system)
- return Response(string)
- return renderer
-
- if __name__ == '__main__':
-
- def view1(request):
- return {'a':1}
-
- def view2(request):
- return {'a':2}
-
- renderer_factory = SampleRendererFactory(search_path=['/a', '/b'])
-
- package_name = 'some.package'
-
- for view, path in [
- (view1, 'templates/foo.pt'),
- (view2, 'templates/bar.pt'),
- ]:
- renderer = renderer_factory(path, dict(package_name=package_name))
- register_renderer_for_view(renderer, view)
-
-This is mostly an amelioration for the first and second bullet points
-above. The second two bullet points are not addressed by it.
-
-Also: think about generalizing this a bit into something which wraps a
-view callable like a decorator, which can influence input and output.
diff --git a/docs/api.rst b/docs/api.rst
index a97c79fa9..3bdb323ca 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -19,6 +19,7 @@ documentation is organized alphabetically by module name.
api/interfaces
api/location
api/paster
+ api/renderers
api/router
api/scripting
api/security
diff --git a/docs/api/configuration.rst b/docs/api/configuration.rst
index 68e3b8724..f555b18f0 100644
--- a/docs/api/configuration.rst
+++ b/docs/api/configuration.rst
@@ -5,7 +5,7 @@
.. automodule:: repoze.bfg.configuration
- .. 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)
+ .. 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)
.. attribute:: registry
@@ -20,7 +20,9 @@
.. automethod:: unhook_zca()
- .. automethod:: setup_registry(settings=None, root_factory=None, authentication_policy=None, renderers=DEFAULT_RENDERERS, debug_logger=None)
+ .. automethod:: get_settings
+
+ .. automethod:: setup_registry(settings=None, root_factory=None, authentication_policy=None, renderers=DEFAULT_RENDERERS, debug_logger=None, locale_negotiator=None, request_factory=None, renderer_globals_factory=None)
.. automethod:: add_renderer(name, factory)
@@ -54,11 +56,13 @@
.. automethod:: set_request_factory
+ .. automethod:: set_renderer_globals_factory
+
.. automethod:: testing_securitypolicy
.. automethod:: testing_models
.. automethod:: testing_add_subscriber
- .. automethod:: testing_add_template
+ .. automethod:: testing_add_renderer
diff --git a/docs/api/renderers.rst b/docs/api/renderers.rst
new file mode 100644
index 000000000..775b8d8d8
--- /dev/null
+++ b/docs/api/renderers.rst
@@ -0,0 +1,13 @@
+.. _renderers_module:
+
+:mod:`repoze.bfg.renderers`
+---------------------------
+
+.. module:: repoze.bfg.renderers
+
+.. autofunction:: get_renderer
+
+.. autofunction:: render
+
+.. autofunction:: render_to_response
+
diff --git a/docs/glossary.rst b/docs/glossary.rst
index a1adec50b..ce0d57111 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -756,3 +756,8 @@ Glossary
cultural context. Often shortened to "l10" (because the word
"localization" is L, 10 letters, then N). See also:
:term:`Internationalization`.
+
+ renderer globals
+ Values injected as names into a renderer based on application
+ policy. See :ref:`adding_renderer_globals` for more
+ information.
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 0614b48fd..89f126b07 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -394,6 +394,72 @@ method:
config = Configurator()
config.set_request_factory(MyRequestFactory)
+.. _adding_renderer_globals:
+
+Adding Renderer Globals
+-----------------------
+
+Whenever :mod:`repoze.bfg` handles a request to perform a rendering
+(after a view with a ``renderer=`` configuration attribute is invoked,
+or when the any of the methods beginning with ``render`` within the
+:mod:`repoze.bfg.renderers` module are called, *renderer globals* can
+be injected into the *system* values sent to the renderer. By
+default, no renderer globals are injected, and the "bare" system
+values (such as ``request``, ``context``, and ``renderer_name``) are
+the only values present in the system dictionary passed to every
+renderer.
+
+A callback that :mod:`repoze.bfg` will call every time a renderer is
+invoked can be added by passing a ``renderer_globals_factory``
+argument to the constructor of the :term:`configurator`.
+
+.. code-block:: python
+ :linenos:
+
+ def globals_factory(system):
+ return {'a':1}
+
+ config = Configurator(renderer_globals_factory=globals_factory)
+
+Such a callback must accept a single positional argument (notionally
+named ``system``) which will contain the original system values. It
+must return a dictionary of values that will be merged into the system
+dictionary.
+
+A renderer globals factory can alternately be registered via ZCML as a
+through the use of the ZCML ``utility`` directive. In the below, we
+assume a ``globals_factory`` function lives in a package named
+``mypackage.mymodule``.
+
+.. code-block:: xml
+ :linenos:
+
+ <utility
+ component="mypackage.mymodule.renderer_globals_factory"
+ provides="repoze.bfg.interfaces.IRendererGlobalsFactory"
+ />
+
+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:`repoze.bfg.configuration.Configurator.set_renderer_globals_factory`
+method:
+
+.. code-block:: python
+ :linenos:
+
+ from repoze.bfg.configuration import Configurator
+ from repoze.bfg.request import Request
+
+.. code-block:: python
+ :linenos:
+
+ def globals_factory(system):
+ return {'a':1}
+
+ config = Configurator()
+ config.set_renderer_globals_factory(globals_factory)
+
.. _registering_configuration_decorators:
Registering Configuration Decorators
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index e289df1f9..dce0733b6 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -31,46 +31,109 @@ The most straightforward way to use a template within
given templating engine to do so.
:mod:`repoze.bfg` provides various APIs that allow you to render
-:term:`Chameleon` templates directly from within a view callable. For
-example, if there is a :term:`Chameleon` ZPT template named ``foo.pt``
-in a directory in your application named ``templates``, you can render
-the template from within the body of a view callable like so:
+templates directly from within a view callable. For example, if there
+is a :term:`Chameleon` ZPT template named ``foo.pt`` in a directory in
+your application named ``templates``, you can render the template from
+within the body of a view callable like so:
.. code-block:: python
:linenos:
- from repoze.bfg.chameleon_zpt import render_template_to_response
+ from repoze.bfg.renderers import render_to_response
def sample_view(request):
- return render_template_to_response('templates/foo.pt', foo=1, bar=2)
+ return render_to_response('templates/foo.pt', foo=1, bar=2,
+ request=request)
+
+.. warning:: Earlier iterations of this documentation
+.. (pre-version-1.3) encouraged the application developer to use
+ ZPT-specific APIs such as
+ :func:`repoze.bfg.chameleon_zpt.render_template_to_response`,
+ :func:`repoze.bfg.chameleon_zpt.render_template_to_iterable`, and
+ :func:`repoze.bfg.chameleon_zpt.render_template` to render
+ templates directly. This style of rendering still works, but at
+ least for purposes of this documentation, those functions are
+ deprecated. Application developers are encouraged instead to use
+ the functions available in the :mod:`repoze.bfg.renderers` module
+ to perform rendering tasks. This set of functions works to render
+ templates for all renderer extensions registered with
+ :mod:`repoze.bfg`.
The ``sample_view`` :term:`view callable` above returns a
:term:`response` object which contains the body of the
-``template/foo.pt`` template. The template author will have the names
+``templates/foo.pt`` template. In this case, the ``templates``
+directory should live in the same directory as the module containing
+the ``sample_view`` function. The template author will have the names
``foo`` and ``bar`` available as top-level names for replacement or
comparison purposes.
+In the example above, the path ``templates/foo.pt`` is relative to the
+directory in which the file which defines the view configuration
+lives. In this case, this is the directory containing the file that
+defines the ``sample_view`` function. Although a renderer path is
+usually just a simple relative pathname, a path named as a renderer
+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 another package. For
+example:
+
+.. code-block:: python
+ :linenos:
+
+ from repoze.bfg.renderers import render_to_response
+
+ def sample_view(request):
+ return render_to_response('mypackage:templates/foo.pt', foo=1, bar=2,
+ request=request)
+
+A resource specification points at a file within a Python *package*.
+In this case, it points at a file named ``foo.pt`` within the
+``templates`` directory of the ``mypackage`` package. Using a
+resource specification instead of a relative template name is usually
+a good idea, because calls to ``render_to_response`` using resource
+specifications will continue to work properly if you move the code
+containing them around.
+
+In the examples above we pass in a keyword argument named ``request``
+representing the current :mod:`repoze.bfg` request. Passing a request
+keyword argument will cause the ``render_to_response`` function to
+supply the renderer with more correct system values (see
+:ref:`renderer_system_values`), because most of the information
+required to compose proper system values is present in the request.
+If you care about the correct system values being provided to the
+renderer being called (in particular, if your template relies on the
+name ``request`` or ``context``, or if you've configured special
+:term:`renderer globals` make sure to pass ``request`` as a keyword
+argument in every call to to a ``repoze.bfg.renderers.render_*``
+function.
+
Every view must return a :term:`response` object (except for views
-which use a :term:`renderer`, which we'll see shortly). The
-:func:`repoze.bfg.chameleon_zpt.render_template_to_response` function
-is a shortcut function that actually returns a response object, but
-not all template APIs know about responses. When you use a template
-API that is "response-ignorant" you can also easily render a template
-to a string, and construct your own response object as necessary with
-the string as the body.
-
-For example, the :func:`repoze.bfg.chameleon_zpt.render_template` API
-returns a string. We can manufacture a :term:`response` object
-directly, and use that string as the body of the response:
+which use a :term:`renderer` named via view configuration, which we'll
+see shortly). The :func:`repoze.bfg.renders.render_to_response`
+function is a shortcut function that actually returns a response
+object.
+
+Obviously not all APIs you might call to get respnonse data will
+return a response object. If you call a "response-ignorant" API that
+returns information you'd like to use as a response (such as when you
+render a template to a string), you must construct your own response
+object as necessary with the string as the body. For example, the
+:func:`repoze.bfg.renderers.render` API returns a string. We can
+manufacture a :term:`response` object directly, and use that string as
+the body of the response:
.. code-block:: python
:linenos:
- from repoze.bfg.chameleon_zpt import render_template
+ from repoze.bfg.renderers import render
from webob import Response
def sample_view(request):
- result = render_template('templates/foo.pt', foo=1, bar=2)
+ result = render('mypackage:templates/foo.pt', foo=1, bar=2,
+ request=request)
response = Response(result)
return response
@@ -83,7 +146,7 @@ functions into your views module, use those APIs to generate a string,
then return that string as the body of a :term:`WebOb`
:term:`Response` object.
-For example, here's an example of using `Mako
+For example, here's an example of using raw `Mako
<http://www.makotemplates.org/>`_ from within a :mod:`repoze.bfg`
:term:`view`:
@@ -100,6 +163,13 @@ For example, here's an example of using `Mako
response = Response(result)
return response
+You probably wouldn't use this particular snippet in a project,
+because it's easier to use the Mako renderer bindings which already
+exist for :mod:`repoze.bfg` named ``repoze.bfg.mako`` (available from
+`PyPI <http://pypi.python.org/pypi/repoze.bfg.mako>`_). But if your
+favorite templating system is not supported as a renderer extension
+for BFG, you can create your own simple conmbination as shown above.
+
.. note::
If you use third-party templating languages without cooperating BFG
@@ -121,29 +191,31 @@ may set attributes on the response that influence these values.
Here's an example of changing the content-type and status of the
response object returned by
-:func:`repoze.bfg.chameleon_zpt.render_template_to_response`:
+:func:`repoze.bfg.renderers.render_to_response`:
.. code-block:: python
:linenos:
- from repoze.bfg.chameleon_zpt import render_template_to_response
+ from repoze.bfg.renderers.render_to_response
def sample_view(request):
- response = render_template_to_response('templates/foo.pt', foo=1, bar=2)
+ response = render_to_response('templates/foo.pt', foo=1, bar=2,
+ request=request)
response.content_type = 'text/plain'
response.status_int = 204
return response
-Here's an example of manufacturing a response object using the result of
-:func:`repoze.bfg.chameleon_zpt.render_template` (a string):
+Here's an example of manufacturing a response object using the result
+of :func:`repoze.bfg.renderers.render` (a string):
.. code-block:: python
:linenos:
- from repoze.bfg.chameleon_zpt import render_template
+ from repoze.bfg.renderers import render
from webob import Response
def sample_view(request):
- result = render_template('templates/foo.pt', foo=1, bar=2)
+ result = render('mypackage:templates/foo.pt', foo=1, bar=2,
+ request=request)
response = Response(result)
response.content_type = 'text/plain'
return response
@@ -153,22 +225,50 @@ Here's an example of manufacturing a response object using the result of
single: template renderers
single: renderer (template)
+
+.. _renderer_system_values:
+
+System Values Used During Rendering
+-----------------------------------
+
+When a template is rendered using
+:func:`repoze.bfg.render_to_response` or :func:`repoze.bfg.render`,
+the renderer representing the template will be provided with a number
+of *system* values. These values are provided in a dictionary to the
+renderer and include:
+
+``context``
+ The current :mod:`repoze.bfg` context if ``request`` was provided as
+ a keyword argument or ``None``.
+
+``request``
+ The request provided as a keyword argument.
+
+``renderer_name``
+ The renderer name used to perform the rendering,
+ e.g. ``mypackage:templates/foo.pt``.
+
+What any particular renderer does with them is up to the renderer
+itself, but most renderers, including al Chameleon renderers, make
+these names available as top-level template variables.
+
.. _templates_used_as_renderers:
-Templates Used as Renderers
----------------------------
+Templates Used as Renderers via Configuration
+---------------------------------------------
-Instead of using templating system APIs within the body of a view
-function directly to render a specific template, you may associate a
-template written in a supported templating language with a view
-indirectly by specifying it as a :term:`renderer`.
+Instead of using the :func:`repoze.bfg.renderers.render_to_response`
+API within the body of a view function directly to render a specific
+template to a response, you may associate a template written in a
+supported templating language with a view indirectly by specifying it
+as a :term:`renderer` in *view configuration*.
-To use a renderer, specify a template :term:`resource specification`
-as the ``renderer`` argument or attribute to the :term:`view
-configuration` of a :term:`view callable`. Then return a *dictionary*
-from that view callable. The dictionary items returned by the view
-callable will be made available to the renderer template as top-level
-names.
+To use a renderer via view configuration, specify a template
+:term:`resource specification` as the ``renderer`` argument or
+attribute to the :term:`view configuration` of a :term:`view
+callable`. Then return a *dictionary* from that view callable. The
+dictionary items returned by the view callable will be made available
+to the renderer template as top-level names.
The association of a template as a renderer for a :term:`view
configuration` makes it possible to replace code within a :term:`view
@@ -187,6 +287,18 @@ template renderer:
def my_view(request):
return {'foo':1, 'bar':2}
+.. note:: It is not necessary to supply the ``request`` value as a key
+ in the dictionary result returned from a renderer-configured view
+ callable in order to ensure that the "most correct" system values
+ are supplied to the renderer as it is when you use
+ :func:`repoze.bfg.renderers.render` or
+ :func:`repoze.bfg.renderers.render_to_response`. This is handled
+ automatically.
+
+Similar renderer configuration can be done imperatively and via
+:term:`ZCML`. See :ref:`views_which_use_a_renderer`. See also
+:ref:`built_in_renderers`.
+
The ``renderer`` argument to the ``@bfg_view`` configuration decorator
shown above is the template *path*. In the example above, the path
``templates/foo.pt`` is *relative*. Relative to what, you ask?
@@ -201,13 +313,6 @@ a :term:`resource specification` in the form
``some.dotted.package_name:relative/path``, making it possible to
address template resources which live in another package.
-When a template :term:`renderer` is used to render the result of a
-view callable, several names are passed into the template as top-level
-names by default, including ``context`` and ``request``. Similar
-renderer configuration can be done imperatively and via :term:`ZCML`.
-See :ref:`views_which_use_a_renderer`. See also
-:ref:`built_in_renderers`.
-
Not just any template from any arbitrary templating system may be used
as a renderer. Bindings must exist specifically for :mod:`repoze.bfg`
to use a templating language template as a renderer. Currently,
@@ -217,17 +322,18 @@ discussion of their details. :mod:`repoze.bfg` also supports the use
of :term:`Jinja2` templates as renderers. See
:ref:`available_template_system_bindings`.
-.. sidebar:: Why Use A Renderer
-
- Using a renderer is usually a better way to render templates than
- using any templating API directly from within a :term:`view
- callable` because it makes the view callable more unit-testable.
- Views which use templating APIs directly must return a
- :term:`Response` object. Making testing assertions about response
- objects is typically an indirect process, because it means that
- your test code often needs to somehow parse information
- out of the response body (often HTML). View callables which use
- renderers typically return a dictionary, and making assertions
+.. sidebar:: Why Use A Renderer via View Configuration
+
+ Using a renderer in view configuration is usually a better way to
+ render templates than using any rendering API directly from within
+ a :term:`view callable` because it makes the view callable more
+ unit-testable. Views which use templating or rendering APIs
+ directly must return a :term:`Response` object. Making testing
+ assertions about response objects is typically an indirect process,
+ because it means that your test code often needs to somehow parse
+ information out of the response body (often HTML). View callables
+ which are configured with renderers externally via view
+ configuration typically return a dictionary, and making assertions
about the information is almost always more direct than needing to
parse HTML. Specifying a renderer from within :term:`ZCML` (as
opposed to imperatively or via a ``bfg_view`` decorator, or using a
@@ -244,6 +350,11 @@ status attributes, you must set attributes on the *request* object
within the view before returning the dictionary. See
:ref:`response_request_attrs` for more information.
+The same set of system values are provided to templates rendered via a
+rendere view configuration as those provided to templates rendered
+imperatively. See :ref:`renderer_system_values`.
+
+
.. index::
single: Chameleon ZPT templates
single: ZPT templates (Chameleon)
@@ -285,10 +396,6 @@ the template as a :term:`renderer` like so:
def my_view(request):
return {'foo':1, 'bar':2}
-If you'd rather use templates directly within a view callable (without
-the indirection of using a renderer), see :ref:`chameleon_zpt_module`
-for the API description.
-
See also :ref:`built_in_renderers` for more general information about
renderers, including Chameleon ZPT renderers.
@@ -323,9 +430,8 @@ Here's what a simple :term:`Chameleon` ZPT template used under
Note the use of :term:`Genshi` -style ``${replacements}`` above. This
is one of the ways that :term:`Chameleon` ZPT differs from standard
ZPT. The above template expects to find a ``project`` key in the set
-of keywords passed in to it via
-:func:`repoze.bfg.chameleon_zpt.render_template` or
-:func:`repoze.bfg.render_template_to_response`. Typical ZPT
+of keywords passed in to it via :func:`repoze.bfg.renderers.render` or
+:func:`repoze.bfg.renderers.render_to_response`. Typical ZPT
attribute-based syntax (e.g. ``tal:content`` and ``tal:replace``) also
works in these templates.
diff --git a/repoze/bfg/chameleon_text.py b/repoze/bfg/chameleon_text.py
index 7aca29529..bbf15ead1 100644
--- a/repoze/bfg/chameleon_text.py
+++ b/repoze/bfg/chameleon_text.py
@@ -1,7 +1,5 @@
import sys
-from webob import Response
-
from zope.interface import implements
try:
@@ -20,12 +18,12 @@ except ImportError: # pragma: no cover
class Parser(object):
pass
-from repoze.bfg.interfaces import IResponseFactory
from repoze.bfg.interfaces import ITemplateRenderer
from repoze.bfg.interfaces import IChameleonTranslate
from repoze.bfg.decorator import reify
-from repoze.bfg.renderers import template_renderer_factory
+from repoze.bfg import renderers
+from repoze.bfg.path import caller_package
from repoze.bfg.settings import get_settings
from repoze.bfg.threadlocal import get_current_registry
@@ -40,7 +38,7 @@ class TextTemplateFile(TemplateFile):
doctype, **kwargs)
def renderer_factory(path):
- return template_renderer_factory(path, TextTemplateRenderer)
+ return renderers.template_renderer_factory(path, TextTemplateRenderer)
class TextTemplateRenderer(object):
implements(ITemplateRenderer)
@@ -82,15 +80,26 @@ def get_renderer(path):
:term:`Chameleon` text template using the template implied by the
``path`` argument. The ``path`` argument may be a
package-relative path, an absolute path, or a :term:`resource
- specification`."""
- return renderer_factory(path)
+ specification`.
+
+ .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use
+ :func:`repoze.bfg.renderers.get_renderer` instead.
+ """
+ package = caller_package()
+ return renderers.renderer_from_name(path, package)
def get_template(path):
""" Return the underyling object representing a :term:`Chameleon`
text template using the template implied by the ``path`` argument.
The ``path`` argument may be a package-relative path, an absolute
- path, or a :term:`resource specification`."""
- renderer = renderer_factory(path)
+ path, or a :term:`resource specification`.
+
+ .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use
+ the ``implementation()`` method of a template renderer retrieved via
+ :func:`repoze.bfg.renderers.get_renderer` instead.
+ """
+ package = caller_package()
+ renderer = renderers.renderer_from_name(path, package)
return renderer.implementation()
def render_template(path, **kw):
@@ -99,9 +108,13 @@ def render_template(path, **kw):
package-relative path, an absolute path, or a :term:`resource
specification`. The arguments in ``*kw`` are passed as top-level
names to the template, and so may be used within the template
- itself. Returns a string."""
- renderer = renderer_factory(path)
- return renderer(kw, {})
+ itself. Returns a string.
+
+ .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use
+ :func:`repoze.bfg.renderers.render` instead.
+ """
+ package = caller_package()
+ return renderers._render(path, None, kw, {}, None, package)
def render_template_to_response(path, **kw):
""" Render a :term:`Chameleon` text template using the template
@@ -110,9 +123,10 @@ def render_template_to_response(path, **kw):
specification`. The arguments in ``*kw`` are passed as top-level
names to the template, and so may be used within the template
itself. Returns a :term:`Response` object with the body as the
- template result.."""
- renderer = renderer_factory(path)
- result = renderer(kw, {})
- reg = get_current_registry()
- response_factory = reg.queryUtility(IResponseFactory, default=Response)
- return response_factory(result)
+ template result.
+
+ .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use
+ :func:`repoze.bfg.renderers.render_to_response` instead.
+ """
+ package = caller_package()
+ return renderers._render_to_response(path, None, kw, {}, None, package)
diff --git a/repoze/bfg/chameleon_zpt.py b/repoze/bfg/chameleon_zpt.py
index ba4c9863f..494db43d2 100644
--- a/repoze/bfg/chameleon_zpt.py
+++ b/repoze/bfg/chameleon_zpt.py
@@ -1,7 +1,5 @@
import sys
-from webob import Response
-
from zope.interface import implements
try:
@@ -14,16 +12,16 @@ except ImportError: # pragma: no cover
raise ImportError, exc, tb
from repoze.bfg.interfaces import IChameleonTranslate
-from repoze.bfg.interfaces import IResponseFactory
from repoze.bfg.interfaces import ITemplateRenderer
from repoze.bfg.decorator import reify
-from repoze.bfg.renderers import template_renderer_factory
+from repoze.bfg.path import caller_package
+from repoze.bfg import renderers
from repoze.bfg.settings import get_settings
from repoze.bfg.threadlocal import get_current_registry
def renderer_factory(path):
- return template_renderer_factory(path, ZPTTemplateRenderer)
+ return renderers.template_renderer_factory(path, ZPTTemplateRenderer)
class ZPTTemplateRenderer(object):
implements(ITemplateRenderer)
@@ -65,15 +63,26 @@ def get_renderer(path):
:term:`Chameleon` ZPT template using the template implied by the
``path`` argument. The ``path`` argument may be a
package-relative path, an absolute path, or a :term:`resource
- specification`."""
- return renderer_factory(path)
+ specification`.
+
+ .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use
+ :func:`repoze.bfg.renderers.get_renderer` instead.
+ """
+ package = caller_package()
+ return renderers.renderer_from_name(path, package)
def get_template(path):
""" Return the underlying object representing a :term:`Chameleon`
ZPT template using the template implied by the ``path`` argument.
The ``path`` argument may be a package-relative path, an absolute
- path, or a :term:`resource specification`."""
- renderer = renderer_factory(path)
+ path, or a :term:`resource specification`.
+
+ .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use
+ the ``implementation()`` method of a template renderer retrieved via
+ :func:`repoze.bfg.renderers.get_renderer` instead.
+ """
+ package = caller_package()
+ renderer = renderers.renderer_from_name(path, package)
return renderer.implementation()
def render_template(path, **kw):
@@ -82,9 +91,13 @@ def render_template(path, **kw):
package-relative path, an absolute path, or a :term:`resource
specification`. The arguments in ``*kw`` are passed as top-level
names to the template, and so may be used within the template
- itself. Returns a string."""
- renderer = renderer_factory(path)
- return renderer(kw, {})
+ itself. Returns a string.
+
+ .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use
+ :func:`repoze.bfg.renderers.render` instead.
+ """
+ package = caller_package()
+ return renderers._render(path, None, kw, {}, None, package)
def render_template_to_response(path, **kw):
""" Render a :term:`Chameleon` ZPT template using the template
@@ -93,10 +106,11 @@ def render_template_to_response(path, **kw):
specification`. The arguments in ``*kw`` are passed as top-level
names to the template, and so may be used within the template
itself. Returns a :term:`Response` object with the body as the
- template result.."""
- renderer = renderer_factory(path)
- result = renderer(kw, {})
- reg = get_current_registry()
- response_factory = reg.queryUtility(IResponseFactory, default=Response)
- return response_factory(result)
+ template result.
+
+ .. warning:: This API is deprecated in :mod:`repoze.bfg` 1.3. Use
+ :func:`repoze.bfg.renderers.render_to_response` instead.
+ """
+ package = caller_package()
+ return renderers._render_to_response(path, None, kw, {}, None, package)
diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py
index 443da9d61..124ce076a 100644
--- a/repoze/bfg/configuration.py
+++ b/repoze/bfg/configuration.py
@@ -4,7 +4,6 @@ import sys
import threading
import inspect
-from webob import Response
import venusian
from translationstring import ChameleonTranslate
@@ -26,16 +25,15 @@ from repoze.bfg.interfaces import ILocaleNegotiator
from repoze.bfg.interfaces import IMultiView
from repoze.bfg.interfaces import IPackageOverrides
from repoze.bfg.interfaces import IRendererFactory
+from repoze.bfg.interfaces import IRendererGlobalsFactory
from repoze.bfg.interfaces import IRequest
from repoze.bfg.interfaces import IRequestFactory
-from repoze.bfg.interfaces import IResponseFactory
from repoze.bfg.interfaces import IRootFactory
from repoze.bfg.interfaces import IRouteRequest
from repoze.bfg.interfaces import IRoutesMapper
from repoze.bfg.interfaces import ISecuredView
from repoze.bfg.interfaces import ISettings
from repoze.bfg.interfaces import IStaticURLInfo
-from repoze.bfg.interfaces import ITemplateRenderer
from repoze.bfg.interfaces import ITranslationDirectories
from repoze.bfg.interfaces import ITraverser
from repoze.bfg.interfaces import IView
@@ -44,6 +42,7 @@ from repoze.bfg.interfaces import IViewClassifier
from repoze.bfg import chameleon_text
from repoze.bfg import chameleon_zpt
from repoze.bfg import renderers
+from repoze.bfg.renderers import _render_to_response
from repoze.bfg.authorization import ACLAuthorizationPolicy
from repoze.bfg.compat import all
from repoze.bfg.compat import md5
@@ -91,7 +90,8 @@ class Configurator(object):
The Configurator accepts a number of arguments: ``registry``,
``package``, ``settings``, ``root_factory``,
``authentication_policy``, ``authorization_policy``, ``renderers``
- ``debug_logger``, ``locale_negotiator``, and ``request_factory``.
+ ``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:`repoze.bfg.registry.Registry`
@@ -118,7 +118,8 @@ class Configurator(object):
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
- :func:`repoze.bfg.settings.get_settings` API.
+ :meth:`repoze.bfg.configuration.Configurator.get_settings` and
+ :func:`repoze.bfg.settings.get_settings` APIs.
If the ``root_factory`` argument is passed, it should be an object
representing the default :term:`root factory` for your
@@ -150,28 +151,21 @@ class Configurator(object):
:term:`locale negotiator` implementation. See
:ref:`custom_locale_negotiator`.
- If ``request_factory`` is passed, it should be an object that implements
- the same methods and attributes as the :class:`repoze.bfg.request.Request`
- class (particularly ``__call__`` and ``blank``). This will be the
- factory used by the :mod:`repoze.bfg` router to create all request
- objects. If this attribute is ``None``,
- the :class:`repoze.bfg.request.Request` class will be used as the
- request factory.
-
- .. note:: The
- :meth:`repoze.bfg.configuration.Configurator.set_request_factory`
- method can be used to achieve the same purpose as passing
- ``request_factory``to the Configurator constructor any time after the
- configurator has been constructed.
"""
manager = manager # for testing injection
venusian = venusian # for testing injection
- def __init__(self, registry=None, package=None, settings=None,
- root_factory=None, authentication_policy=None,
- authorization_policy=None, renderers=DEFAULT_RENDERERS,
+ 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):
+ request_factory=None,
+ renderer_globals_factory=None):
self.package = package or caller_package()
self.registry = registry
if registry is None:
@@ -185,7 +179,9 @@ class Configurator(object):
renderers=renderers,
debug_logger=debug_logger,
locale_negotiator=locale_negotiator,
- request_factory=request_factory)
+ request_factory=request_factory,
+ renderer_globals_factory=renderer_globals_factory
+ )
def _set_settings(self, mapping):
settings = Settings(mapping or {})
@@ -200,15 +196,8 @@ class Configurator(object):
factory = DefaultRootFactory
self.registry.registerUtility(factory, IRootFactory)
self.registry.registerUtility(factory, IDefaultRootFactory) # b/c
-
- def _renderer_from_name(self, path_or_spec):
- if path_or_spec is None:
- # check for global default renderer
- factory = self.registry.queryUtility(IRendererFactory)
- if factory is not None:
- return factory(path_or_spec)
- return None
+ def _renderer_factory_from_name(self, path_or_spec):
if '.' in path_or_spec:
name = os.path.splitext(path_or_spec)[1]
spec = self._make_spec(path_or_spec)
@@ -217,8 +206,21 @@ class Configurator(object):
spec = path_or_spec
factory = self.registry.queryUtility(IRendererFactory, name=name)
+ return name, spec, factory
+
+ def _renderer_from_name(self, path_or_spec):
+ if path_or_spec is None:
+ # check for global default renderer
+ factory = self.registry.queryUtility(IRendererFactory)
+ if factory is not None:
+ return factory(path_or_spec)
+ return None
+
+ name, spec, factory = self._renderer_factory_from_name(path_or_spec)
if factory is None:
- raise ValueError('No renderer for renderer name %r' % name)
+ raise ValueError(
+ 'No factory for renderer named %r when looking up %s' %
+ (name, spec))
return factory(spec)
def _set_authentication_policy(self, policy, _info=u''):
@@ -372,7 +374,8 @@ class Configurator(object):
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):
+ locale_negotiator=None, request_factory=None,
+ renderer_globals_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
@@ -387,9 +390,10 @@ class Configurator(object):
initialization.
``setup_registry`` configures settings, a root factory,
- security policies, renderers, a debug logger, and a locale
- negotiator using the configurator's current registry, as per
- the descriptions in the Configurator constructor."""
+ 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)
@@ -410,6 +414,8 @@ class Configurator(object):
registry.registerUtility(locale_negotiator, ILocaleNegotiator)
if request_factory:
self.set_request_factory(request_factory)
+ if renderer_globals_factory:
+ self.set_renderer_globals_factory(renderer_globals_factory)
# getSiteManager is a unit testing dep injection
def hook_zca(self, getSiteManager=None):
@@ -477,7 +483,8 @@ class Configurator(object):
Configurator constructor with one or more 'setting' key/value
pairs. A setting is a single key/value pair in the
dictionary-ish object returned from the API
- :func:`repoze.bfg.settings.get_settings`.
+ :func:`repoze.bfg.settings.get_settings` and
+ :meth:`repoze.bfg.configuration.Configurator.get_settings`.
You may pass a dictionary::
@@ -487,9 +494,10 @@ class Configurator(object):
config.add_settings(external_uri='http://example.com')
- This function is useful when you need to test code that
- calls the :func:`repoze.bfg.settings.get_settings` API and which
- uses return values from that API.
+ This function is useful when you need to test code that calls
+ the :func:`repoze.bfg.settings.get_settings` API (or the
+ :meth:`repoze.bfg.configuration.Configurator.get_settings`
+ API) and which uses return values from that API.
.. note:: This method is new as of :mod:`repoze.bfg` 1.2.
"""
@@ -501,6 +509,21 @@ class Configurator(object):
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:`repoze.bfg.configuration.Configurator`
+ constructor or the :func:`repoze.bfg.router.make_app` API.
+
+ .. note:: For backwards compatibility, dictionary keys can also be
+ looked up as attributes of the settings object.
+
+ .. note:: the :class:`repoze.bfg.settings.get_settings` function
+ performs the same duty."""
+ return self.registry.queryUtility(ISettings)
+
def make_wsgi_app(self):
""" Returns a :mod:`repoze.bfg` WSGI application representing
the current configuration state and sends a
@@ -1468,6 +1491,21 @@ class Configurator(object):
"""
self.registry.registerUtility(factory, IRequestFactory)
+ def set_renderer_globals_factory(self, factory):
+ """ The object passed as ``factory`` will be used by the
+ :mod:`repoze.bfg` rendering machinery as a renderers global
+ factory (see :ref:`adding_renderer_globals`). The factory
+ must return a dictionary of items that will be merged intto
+ the *system* dictionary passed in to every renderer used by
+ the application.
+
+ .. note:: Using the :meth:``renderer_globals_factory``
+ argument to the
+ :class:`repoze.bfg.configuration.Configurator` constructor
+ can be used to achieve the same purpose.
+ """
+ self.registry.registerUtility(factory, IRendererGlobalsFactory)
+
def set_locale_negotiator(self, negotiator):
"""
Set the :term:`locale negotiator` for this application. The
@@ -1479,6 +1517,10 @@ class Configurator(object):
more information.
.. note: This API is new as of :mod:`repoze.bfg` version 1.3.
+
+ .. note:: Using the :meth:``locale_negotiator`` argument to
+ the :class:`repoze.bfg.configuration.Configurator`
+ constructor can be used to achieve the same purpose.
"""
self.registry.registerUtility(negotiator, ILocaleNegotiator)
@@ -1712,26 +1754,44 @@ class Configurator(object):
self.add_subscriber(subscriber, event_iface)
return L
- def testing_add_template(self, path, renderer=None):
- """Unit/integration testing helper: register a template
- renderer at ``path`` (usually a relative filename ala
- ``templates/foo.pt``) 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:`repoze.bfg.chameleon_zpt.render_template_to_response`
- function or
- :func:`repoze.bfg.chameleon_text.render_template_to_response`
- function or any other ``render_template*`` API of any built-in
- templating system (see :mod:`repoze.bfg.chameleon_zpt` and
- :mod:`repoze.bfg.chameleon_text`).
+ 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:`repoze.bfg.renderers.render` function or
+ :func:`repoze.bfg.renderers.render_to_response` function or
+ any other ``render_*`` or ``get_*`` API of the
+ :mod:`repoze.bfg.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).
+
+ .. note:: This method is new in :mod:`repoze.bfg` 1.3 (the
+ method named ``testing_add_template`` had the same signature
+ and purpose in previous releases)..
"""
+ from repoze.bfg.testing import DummyRendererFactory
+ name, spec, factory = self._renderer_factory_from_name(path)
+ if factory is None or not isinstance(factory, DummyRendererFactory):
+ factory = DummyRendererFactory(name, factory)
+ self.registry.registerUtility(factory, IRendererFactory, name=name)
+
from repoze.bfg.testing import DummyTemplateRenderer
if renderer is None:
renderer = DummyTemplateRenderer()
- self.registry.registerUtility(renderer, ITemplateRenderer, path)
+ factory.add(spec, 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,
@@ -1996,40 +2056,6 @@ def decorate_view(wrapped_view, original_view):
pass
return True
-def rendered_response(renderer, response, view, context, request,
- renderer_name):
- if ( hasattr(response, 'app_iter') and hasattr(response, 'headerlist')
- and hasattr(response, 'status') ):
- return response
- result = renderer(response, {'view':view, 'renderer_name':renderer_name,
- 'context':context, 'request':request})
- try:
- registry = request.registry
- except AttributeError:
- registry = get_current_registry()
- response_factory = registry.queryUtility(IResponseFactory,
- default=Response)
- response = response_factory(result)
- if request is not None: # in tests, it may be None
- attrs = request.__dict__
- content_type = attrs.get('response_content_type', None)
- if content_type is not None:
- response.content_type = content_type
- headerlist = attrs.get('response_headerlist', None)
- if headerlist is not None:
- for k, v in headerlist:
- response.headers.add(k, v)
- status = attrs.get('response_status', None)
- if status is not None:
- response.status = status
- charset = attrs.get('response_charset', None)
- if charset is not None:
- response.charset = charset
- cache_for = attrs.get('response_cache_for', None)
- if cache_for is not None:
- response.cache_expires = cache_for
- return response
-
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 """
@@ -2073,6 +2099,12 @@ def requestonly(class_or_callable, attr=None):
return False
+def is_response(ob):
+ if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and
+ hasattr(ob, 'status') ):
+ return True
+ return False
+
def _map_view(view, attr=None, renderer=None, renderer_name=None):
wrapped_view = view
@@ -2093,10 +2125,15 @@ def _map_view(view, attr=None, renderer=None, renderer_name=None):
else:
response = getattr(inst, attr)()
if renderer is not None:
- response = rendered_response(renderer,
- response, inst,
- context, request,
- renderer_name)
+ if not is_response(response):
+ system = {'view':inst, 'renderer_name':renderer_name,
+ 'context':context, 'request':request}
+ response = _render_to_response(renderer_name,
+ request,
+ response,
+ system,
+ renderer,
+ None)
return response
wrapped_view = _bfg_class_requestonly_view
else:
@@ -2108,10 +2145,15 @@ def _map_view(view, attr=None, renderer=None, renderer_name=None):
else:
response = getattr(inst, attr)()
if renderer is not None:
- response = rendered_response(renderer,
- response, inst,
- context, request,
- renderer_name)
+ if not is_response(response):
+ system = {'view':inst, 'renderer_name':renderer_name,
+ 'context':context, 'request':request}
+ response = _render_to_response(renderer_name,
+ request,
+ response,
+ system,
+ renderer,
+ None)
return response
wrapped_view = _bfg_class_view
@@ -2125,10 +2167,15 @@ def _map_view(view, attr=None, renderer=None, renderer_name=None):
response = getattr(view, attr)(request)
if renderer is not None:
- response = rendered_response(renderer,
- response, view,
- context, request,
- renderer_name)
+ if not is_response(response):
+ system = {'view':view, 'renderer_name':renderer_name,
+ 'context':context, 'request':request}
+ response = _render_to_response(renderer_name,
+ request,
+ response,
+ system,
+ renderer,
+ None)
return response
wrapped_view = _bfg_requestonly_view
@@ -2136,20 +2183,30 @@ def _map_view(view, attr=None, renderer=None, renderer_name=None):
def _bfg_attr_view(context, request):
response = getattr(view, attr)(context, request)
if renderer is not None:
- response = rendered_response(renderer,
- response, view,
- context, request,
- renderer_name)
+ if not is_response(response):
+ system = {'view':view, 'renderer_name':renderer_name,
+ 'context':context, 'request':request}
+ response = _render_to_response(renderer_name,
+ request,
+ response,
+ system,
+ renderer,
+ None)
return response
wrapped_view = _bfg_attr_view
elif renderer is not None:
def _rendered_view(context, request):
response = view(context, request)
- response = rendered_response(renderer,
- response, view,
- context, request,
- renderer_name)
+ if not is_response(response):
+ system = {'view':view, 'renderer_name':renderer_name,
+ 'context':context, 'request':request}
+ response = _render_to_response(renderer_name,
+ request,
+ response,
+ system,
+ renderer,
+ None)
return response
wrapped_view = _rendered_view
diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py
index 992ec80d9..def957dad 100644
--- a/repoze/bfg/interfaces.py
+++ b/repoze/bfg/interfaces.py
@@ -179,6 +179,15 @@ class IRendererFactory(Interface):
def __call__(name):
""" Return an object that implements ``IRenderer`` """
+class IRendererGlobalsFactory(Interface):
+ def __call__(system_values):
+ """ Return a dictionary of global renderer values (aka
+ top-level template names). The ``system_values`` value passed
+ in will be a dictionary that includes at least a ``request``
+ key, indicating the current request, and the value
+ ``renderer_name``, which will be the name of the renderer in
+ use."""
+
class ITemplateRenderer(IRenderer):
def implementation():
""" Return the object that the underlying templating system
diff --git a/repoze/bfg/renderers.py b/repoze/bfg/renderers.py
index b54803b3b..151044ce5 100644
--- a/repoze/bfg/renderers.py
+++ b/repoze/bfg/renderers.py
@@ -1,6 +1,12 @@
import os
import pkg_resources
+from webob import Response
+
+from zope.deprecation import deprecated
+
+from repoze.bfg.interfaces import IRendererGlobalsFactory
+from repoze.bfg.interfaces import IResponseFactory
from repoze.bfg.interfaces import ITemplateRenderer
from repoze.bfg.compat import json
@@ -8,7 +14,78 @@ from repoze.bfg.path import caller_package
from repoze.bfg.settings import get_settings
from repoze.bfg.threadlocal import get_current_registry
-# concrete renderer factory implementations
+# API
+
+def render(_renderer_name, **values):
+ """ Using the renderer specified as ``renderer_name`` (a template
+ or a static renderer) render the set of values present in
+ ``**values``. Return the result of the renderer's ``__call__``
+ method (usually a string or Unicode).
+
+ If the renderer name refers to a file on disk (such as when the
+ renderer is a template), it's usually best to supply the name as a
+ :term:`resource specification`. You may supply a relative
+ filename as renderer name; it will be converted to a resource
+ specification by combining the package name of the *caller* of
+ this function with the relative filename.
+
+ The ``values`` provided will be supplied as top-level names to the
+ renderer. These will be augmented by a basic set of top-level
+ system names, such as ``request``, ``context``, and
+ ``renderer_name` unless any of these names is already provided
+ within ``*values``. If :term:`renderer globals` have been
+ specified, these will also be used to agument the value.
+
+ Supply a ``request`` parameter containing the current
+ :mod:`repoze.bfg` request as part of ``**values`` in order to
+ provide the renderer with the most correct 'system' values
+ (``request`` and ``context`` in particular).
+
+ .. note:: This API is new in :mod:`repoze.bfg` 1.3.
+
+ """
+ package = caller_package()
+ request = values.pop('request', None)
+ return _render(_renderer_name, request, values, None, None, package)
+
+def render_to_response(_renderer_name, **values):
+ """ Using the renderer specified as ``renderer_name`` (a template
+ or a static renderer) render the set of values present in
+ ``**values``. Return a :term:`Response` object wrapping the result
+ of of the renderer's ``__call__`` method.
+
+ If the renderer name refers to a file on disk (such as when the
+ renderer is a template), it's usually best to supply the name as a
+ :term:`resource specification`. You may supply a relative
+ filename as renderer name; it will be converted to a resource
+ specification by combining the package name of the *caller* of
+ this function with the relative filename.
+
+ The ``values`` provided will be supplied as top-level names to the
+ renderer. These will be augmented by a basic set of top-level
+ system names, such as ``request``, ``context``, and
+ ``renderer_name` unless any of these names is already provided
+ within ``*values``. If :term:`renderer globals` have been
+ specified, these will also be used to agument the value.
+
+ Supply a ``request`` parameter containing the current
+ :mod:`repoze.bfg` request as part of ``**values`` in order to
+ provide the renderer with the most correct 'system' values
+ (``request`` and ``context`` in particular).
+
+ .. note:: This API is new in :mod:`repoze.bfg` 1.3.
+ """
+ package = caller_package()
+ request = values.pop('request', None)
+ return _render_to_response(_renderer_name, request, values, None, None,
+ package)
+
+def get_renderer(spec):
+ """ Return the renderer object for the renderer named as ``spec`` """
+ package = caller_package()
+ return renderer_from_name(spec, package)
+
+# concrete renderer factory implementations (also API)
def json_renderer_factory(name):
def _render(value, system):
@@ -30,7 +107,7 @@ def string_renderer_factory(name):
return value
return _render
-# utility functions
+# utility functions, not API
def template_renderer_factory(spec, impl):
reg = get_current_registry()
@@ -49,8 +126,8 @@ def template_renderer_factory(spec, impl):
try:
package_name, filename = spec.split(':', 1)
except ValueError: # pragma: no cover
- # unit test or somehow we were passed a relative pathname;
- # this should die
+ # somehow we were passed a relative pathname; this
+ # should die
package_name = caller_package(4).__name__
filename = spec
abspath = pkg_resources.resource_filename(package_name, filename)
@@ -64,12 +141,99 @@ def template_renderer_factory(spec, impl):
return renderer
-def renderer_from_name(path):
+def _reload_resources():
+ settings = get_settings()
+ return settings and settings.get('reload_resources')
+
+def renderer_from_name(path, package=None):
from repoze.bfg.configuration import Configurator
reg = get_current_registry()
- config = Configurator(reg)
+ config = Configurator(reg, package=package)
return config._renderer_from_name(path)
-def _reload_resources():
- settings = get_settings()
- return settings and settings.get('reload_resources')
+def _render(renderer_name, request, values, system_values, renderer, package):
+ try:
+ registry = request.registry
+ except AttributeError:
+ registry = get_current_registry()
+
+ if renderer is None:
+ from repoze.bfg.configuration import Configurator
+ config = Configurator(registry, package=package)
+ renderer = config._renderer_from_name(renderer_name)
+
+ if system_values is None:
+ system_values = {
+ 'view':None,
+ 'renderer_name':renderer_name,
+ 'context':getattr(request, 'context', None),
+ 'request':request,
+ }
+
+ globals_factory = registry.queryUtility(IRendererGlobalsFactory)
+
+ if globals_factory is not None:
+ renderer_globals = globals_factory(system_values)
+ if renderer_globals:
+ system_values.update(renderer_globals)
+
+ result = renderer(values, system_values)
+ return result
+
+def _render_to_response(renderer_name, request, values, system_values,
+ renderer, package):
+ result = _render(renderer_name, request, values, system_values, renderer,
+ package)
+ return _make_response(request, result)
+
+def rendered_response(renderer, result, view, context, request, renderer_name):
+ # XXX: deprecated, left here only to not break old code; use
+ # render_to_response instead
+ if ( hasattr(result, 'app_iter') and hasattr(result, 'headerlist')
+ and hasattr(result, 'status') ):
+ return result
+
+ system = {'view':view, 'renderer_name':renderer_name,
+ 'context':context, 'request':request}
+
+ return _render_to_response(renderer_name, request, result, system, renderer,
+ None)
+
+deprecated('rendered_response',
+ "('repoze.bfg.renderers.rendered_response' is not a public API; it is "
+ "officially deprecated as of repoze.bfg 1.3; "
+ "use repoze.bfg.renderers.render_to_response instead')",
+ )
+
+def _make_response(request, result):
+ try:
+ registry = request.registry
+ except AttributeError:
+ registry = get_current_registry()
+
+ response_factory = registry.queryUtility(IResponseFactory,
+ default=Response)
+
+ response = response_factory(result)
+
+ if request is not None:
+ attrs = request.__dict__
+ content_type = attrs.get('response_content_type', None)
+ if content_type is not None:
+ response.content_type = content_type
+ headerlist = attrs.get('response_headerlist', None)
+ if headerlist is not None:
+ for k, v in headerlist:
+ response.headers.add(k, v)
+ status = attrs.get('response_status', None)
+ if status is not None:
+ response.status = status
+ charset = attrs.get('response_charset', None)
+ if charset is not None:
+ response.charset = charset
+ cache_for = attrs.get('response_cache_for', None)
+ if cache_for is not None:
+ response.cache_expires = cache_for
+
+ return response
+
diff --git a/repoze/bfg/settings.py b/repoze/bfg/settings.py
index ff292dc7c..5e1181809 100644
--- a/repoze/bfg/settings.py
+++ b/repoze/bfg/settings.py
@@ -71,6 +71,10 @@ def get_settings():
.. note:: For backwards compatibility, dictionary keys can also be
looked up as attributes of the settings object.
+
+ .. note:: the
+ :class:`repoze.bfg.configuration.Configurator.get_settings` method
+ performs the same duty.
"""
reg = get_current_registry()
return reg.queryUtility(ISettings)
diff --git a/repoze/bfg/testing.py b/repoze/bfg/testing.py
index b68539f43..0d79b559c 100644
--- a/repoze/bfg/testing.py
+++ b/repoze/bfg/testing.py
@@ -1,4 +1,5 @@
import copy
+import types
from webob import Response
@@ -122,12 +123,10 @@ def registerTemplateRenderer(path, renderer=None):
filename ala ``templates/foo.pt``) 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:`repoze.bfg.chameleon_zpt.render_template_to_response`
- function or
- :func:`repoze.bfg.chameleon_text.render_template_to_response`
- function or any other ``render_template*`` API of any built-in
- templating system (see :mod:`repoze.bfg.chameleon_zpt` and
- :mod:`repoze.bfg.chameleon_text`).
+ :func:`repoze.bfg.renderers.render` function or
+ :func:`repoze.bfg.renderers.render_to_response` function or any
+ other ``render_*`` or ``get_*`` API of the
+ :mod:`repoze.bfg.renderers` module.
.. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2.
Instead use the
@@ -140,7 +139,7 @@ def registerTemplateRenderer(path, renderer=None):
return config.testing_add_template(path, renderer)
# registerDummyRenderer is a deprecated alias that should never be removed
-# (far too much usage in the wild)
+# (too much usage in the wild)
registerDummyRenderer = registerTemplateRenderer
def registerView(name, result='', view=None, for_=(Interface, Interface),
@@ -371,7 +370,7 @@ class DummyRootFactory(object):
if 'bfg.routes.matchdict' in request:
self.__dict__.update(request['bfg.routes.matchdict'])
-class DummySecurityPolicy:
+class DummySecurityPolicy(object):
""" A standin for both an IAuthentication and IAuthorization policy """
def __init__(self, userid=None, groupids=(), permissive=True):
self.userid = userid
@@ -401,7 +400,7 @@ class DummySecurityPolicy:
def principals_allowed_by_permission(self, context, permission):
return self.effective_principals(None)
-class DummyTemplateRenderer:
+class DummyTemplateRenderer(object):
"""
An instance of this class is returned from
:func:`repoze.bfg.testing.registerTemplateRenderer`. It has a
@@ -412,14 +411,25 @@ class DummyTemplateRenderer:
def __init__(self, string_response=''):
self._received = {}
- self.string_response = string_response
-
+ self._string_response = string_response
+ self._implementation = MockTemplate(string_response)
+
+ # For in-the-wild test code that doesn't create its own renderer,
+ # but mutates our internals instead. When all you read is the
+ # source code, *everything* is an API!
+ def _get_string_response(self):
+ return self._string_response
+ def _set_string_response(self, response):
+ self._string_response = response
+ self._implementation.response = response
+ string_response = property(_get_string_response, _set_string_response)
+
def implementation(self):
- def callit(**kw):
- return self(kw)
- return callit
+ return self._implementation
def __call__(self, kw, system=None):
+ if system:
+ self._received.update(system)
self._received.update(kw)
return self.string_response
@@ -427,13 +437,15 @@ class DummyTemplateRenderer:
""" Backwards compatibility """
val = self._received.get(k, _marker)
if val is _marker:
- raise AttributeError(k)
+ val = self._implementation._received.get(k, _marker)
+ if val is _marker:
+ raise AttributeError(k)
return val
def assert_(self, **kw):
""" Accept an arbitrary set of assertion key/value pairs. For
each assertion key/value pair assert that the renderer
- (eg. :func:`repoze.bfg.chameleon_zpt.render_template_to_response`)
+ (eg. :func:`repoze.bfg.renderer.render_to_response`)
received the key with a value that equals the asserted
value. If the renderer did not receive the key at all, or the
value received by the renderer doesn't match the assertion
@@ -441,8 +453,11 @@ class DummyTemplateRenderer:
for k, v in kw.items():
myval = self._received.get(k, _marker)
if myval is _marker:
- raise AssertionError(
- 'A value for key "%s" was not passed to the renderer' % k)
+ myval = self._implementation._received.get(k, _marker)
+ if myval is _marker:
+ raise AssertionError(
+ 'A value for key "%s" was not passed to the renderer'
+ % k)
if myval != v:
raise AssertionError(
@@ -533,7 +548,7 @@ class DummyModel:
inst.__parent__ = __parent__
return inst
-class DummyRequest:
+class DummyRequest(object):
""" A dummy request object (imitates a :term:`request` object).
The ``params``, ``environ``, ``headers``, ``path``, and
@@ -675,6 +690,24 @@ def setUp(registry=None, request=None, hook_zca=True):
if registry is None:
registry = Registry('testing')
config = Configurator(registry=registry)
+ 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 repoze.bfg.configuration 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
+ # e.g. ``repoze.bfg.chameleon_zpt.render_template``
+ # without registering a .pt renderer, expecting the "real"
+ # template to be rendered. This is a holdover from when
+ # individual template system renderers weren't indirected
+ # by the ``repoze.bfg.renderers`` machinery, and
+ # ``render_template`` and friends went behind the back of
+ # any existing renderer factory lookup system.
+ config.add_renderer(name, renderer)
hook_zca and config.hook_zca()
config.begin(request=request)
return config
@@ -732,3 +765,48 @@ def cleanUp(*arg, **kw):
extensive production usage, it will never be removed."""
return setUp(*arg, **kw)
+class DummyRendererFactory(object):
+ """ Registered by
+ ``repoze.bfg.configuration.Configurator.testing_add_template`` 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
+ passed to this constructor is usually the *real* template renderer
+ factory, found when ``testing_add_renderer`` is called."""
+ def __init__(self, name, factory):
+ self.name = name
+ self.factory = factory # the "real" renderer factory reg'd previously
+ self.renderers = {}
+
+ def add(self, spec, renderer):
+ self.renderers[spec] = renderer
+ if ':' in spec:
+ package, relative = spec.split(':', 1)
+ self.renderers[relative] = renderer
+
+ def __call__(self, spec):
+ renderer = self.renderers.get(spec)
+ if renderer is None:
+ if ':' in spec:
+ package, relative = spec.split(':', 1)
+ renderer = self.renderers.get(relative)
+ if renderer is None:
+ if self.factory:
+ renderer = self.factory(spec)
+ else:
+ raise KeyError('No testing renderer registered for %r' %
+ spec)
+ return renderer
+
+
+class MockTemplate(object):
+ def __init__(self, response):
+ self._received = {}
+ self.response = response
+ def __getattr__(self, attrname):
+ return self
+ def __getitem__(self, attrname):
+ return self
+ def __call__(self, *arg, **kw):
+ self._received.update(kw)
+ return self.response
diff --git a/repoze/bfg/tests/test_chameleon_text.py b/repoze/bfg/tests/test_chameleon_text.py
index cb30f489a..d9cefbd67 100644
--- a/repoze/bfg/tests/test_chameleon_text.py
+++ b/repoze/bfg/tests/test_chameleon_text.py
@@ -166,88 +166,33 @@ class GetRendererTests(Base, unittest.TestCase):
from repoze.bfg.chameleon_text import get_renderer
return get_renderer(name)
- def test_nonabs_registered(self):
- from repoze.bfg.threadlocal import get_current_registry
- from repoze.bfg.chameleon_text import TextTemplateRenderer
- from repoze.bfg.interfaces import ITemplateRenderer
- minimal = self._getTemplatePath('minimal.txt')
- utility = TextTemplateRenderer(minimal)
- self._registerUtility(utility, ITemplateRenderer, name=minimal)
- result = self._callFUT(minimal)
- self.assertEqual(result, utility)
- reg = get_current_registry()
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), utility)
-
- def test_nonabs_unregistered(self):
- from repoze.bfg.threadlocal import get_current_registry
- from repoze.bfg.chameleon_text import TextTemplateRenderer
- from repoze.bfg.interfaces import ITemplateRenderer
- minimal = self._getTemplatePath('minimal.txt')
- reg = get_current_registry()
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), None)
- utility = TextTemplateRenderer(minimal)
- self._registerUtility(utility, ITemplateRenderer, name=minimal)
- result = self._callFUT(minimal)
- self.assertEqual(result, utility)
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), utility)
-
- def test_explicit_registration(self):
- from repoze.bfg.interfaces import ITemplateRenderer
+ def test_it(self):
+ from repoze.bfg.interfaces import IRendererFactory
class Dummy:
template = object()
- utility = Dummy()
- self._registerUtility(utility, ITemplateRenderer, name='foo')
+ def implementation(self): pass
+ renderer = Dummy()
+ def rf(spec):
+ return renderer
+ self._registerUtility(rf, IRendererFactory, name='foo')
result = self._callFUT('foo')
- self.failUnless(result is utility)
-
-class GetTemplateTests(unittest.TestCase, Base):
- def setUp(self):
- Base.setUp(self)
-
- def tearDown(self):
- Base.tearDown(self)
+ self.failUnless(result is renderer)
+class GetTemplateTests(Base, unittest.TestCase):
def _callFUT(self, name):
from repoze.bfg.chameleon_text import get_template
return get_template(name)
- def test_nonabs_registered(self):
- from repoze.bfg.threadlocal import get_current_registry
- from repoze.bfg.chameleon_text import TextTemplateRenderer
- from repoze.bfg.interfaces import ITemplateRenderer
- minimal = self._getTemplatePath('minimal.txt')
- utility = TextTemplateRenderer(minimal)
- self._registerUtility(utility, ITemplateRenderer, name=minimal)
- result = self._callFUT(minimal)
- self.assertEqual(result.filename, minimal)
- reg = get_current_registry()
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), utility)
-
- def test_nonabs_unregistered(self):
- from repoze.bfg.threadlocal import get_current_registry
- from repoze.bfg.chameleon_text import TextTemplateRenderer
- from repoze.bfg.interfaces import ITemplateRenderer
- minimal = self._getTemplatePath('minimal.txt')
- reg = get_current_registry()
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), None)
- utility = TextTemplateRenderer(minimal)
- self._registerUtility(utility, ITemplateRenderer, name=minimal)
- result = self._callFUT(minimal)
- self.assertEqual(result.filename, minimal)
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), utility)
-
- def test_explicit_registration(self):
- from repoze.bfg.interfaces import ITemplateRenderer
+ def test_it(self):
+ from repoze.bfg.interfaces import IRendererFactory
class Dummy:
template = object()
def implementation(self):
return self.template
- utility = Dummy()
- self._registerUtility(utility, ITemplateRenderer, name='foo')
+ renderer = Dummy()
+ def rf(spec):
+ return renderer
+ self._registerUtility(rf, IRendererFactory, name='foo')
result = self._callFUT('foo')
- self.failUnless(result is utility.template)
-
-
-
-
+ self.failUnless(result is renderer.template)
diff --git a/repoze/bfg/tests/test_chameleon_zpt.py b/repoze/bfg/tests/test_chameleon_zpt.py
index 4ea5fc281..a0f01701a 100644
--- a/repoze/bfg/tests/test_chameleon_zpt.py
+++ b/repoze/bfg/tests/test_chameleon_zpt.py
@@ -128,6 +128,12 @@ class RenderTemplateTests(Base, unittest.TestCase):
'<div xmlns="http://www.w3.org/1999/xhtml">\n</div>')
class RenderTemplateToResponseTests(Base, unittest.TestCase):
+ def setUp(self):
+ cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
def _callFUT(self, name, **kw):
from repoze.bfg.chameleon_zpt import render_template_to_response
return render_template_to_response(name, **kw)
@@ -157,82 +163,32 @@ class GetRendererTests(Base, unittest.TestCase):
from repoze.bfg.chameleon_zpt import get_renderer
return get_renderer(name)
- def test_nonabs_registered(self):
- from repoze.bfg.threadlocal import get_current_registry
- from repoze.bfg.chameleon_zpt import ZPTTemplateRenderer
- from repoze.bfg.interfaces import ITemplateRenderer
- minimal = self._getTemplatePath('minimal.pt')
- utility = ZPTTemplateRenderer(minimal)
- self._registerUtility(utility, ITemplateRenderer, name=minimal)
- result = self._callFUT(minimal)
- self.assertEqual(result, utility)
- reg = get_current_registry()
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), utility)
-
- def test_nonabs_unregistered(self):
- from repoze.bfg.threadlocal import get_current_registry
- from repoze.bfg.chameleon_zpt import ZPTTemplateRenderer
- from repoze.bfg.interfaces import ITemplateRenderer
- minimal = self._getTemplatePath('minimal.pt')
- reg = get_current_registry()
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), None)
- utility = ZPTTemplateRenderer(minimal)
- self._registerUtility(utility, ITemplateRenderer, name=minimal)
- result = self._callFUT(minimal)
- self.assertEqual(result, utility)
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), utility)
-
- def test_explicit_registration(self):
- from repoze.bfg.interfaces import ITemplateRenderer
+ def test_it(self):
+ from repoze.bfg.interfaces import IRendererFactory
class Dummy:
template = object()
- utility = Dummy()
- self._registerUtility(utility, ITemplateRenderer, name='foo')
+ def implementation(self): pass
+ renderer = Dummy()
+ def rf(spec):
+ return renderer
+ self._registerUtility(rf, IRendererFactory, name='foo')
result = self._callFUT('foo')
- self.failUnless(result is utility)
+ self.failUnless(result is renderer)
class GetTemplateTests(Base, unittest.TestCase):
def _callFUT(self, name):
from repoze.bfg.chameleon_zpt import get_template
return get_template(name)
- def test_nonabs_registered(self):
- from repoze.bfg.threadlocal import get_current_registry
- from repoze.bfg.chameleon_zpt import ZPTTemplateRenderer
- from repoze.bfg.interfaces import ITemplateRenderer
- minimal = self._getTemplatePath('minimal.pt')
- utility = ZPTTemplateRenderer(minimal)
- self._registerUtility(utility, ITemplateRenderer, name=minimal)
- result = self._callFUT(minimal)
- self.assertEqual(result.filename, minimal)
- reg = get_current_registry()
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), utility)
-
- def test_nonabs_unregistered(self):
- from repoze.bfg.threadlocal import get_current_registry
- from repoze.bfg.chameleon_zpt import ZPTTemplateRenderer
- from repoze.bfg.interfaces import ITemplateRenderer
- minimal = self._getTemplatePath('minimal.pt')
- reg = get_current_registry()
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), None)
- utility = ZPTTemplateRenderer(minimal)
- self._registerUtility(utility, ITemplateRenderer, name=minimal)
- result = self._callFUT(minimal)
- self.assertEqual(result.filename, minimal)
- self.assertEqual(reg.queryUtility(ITemplateRenderer, minimal), utility)
-
- def test_explicit_registration(self):
- from repoze.bfg.interfaces import ITemplateRenderer
+ def test_it(self):
+ from repoze.bfg.interfaces import IRendererFactory
class Dummy:
template = object()
def implementation(self):
return self.template
- utility = Dummy()
- self._registerUtility(utility, ITemplateRenderer, name='foo')
+ renderer = Dummy()
+ def rf(spec):
+ return renderer
+ self._registerUtility(rf, IRendererFactory, name='foo')
result = self._callFUT('foo')
- self.failUnless(result is utility.template)
-
-
-
-
-
+ self.failUnless(result is renderer.template)
diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py
index 25c5544b6..0cb8ff5e8 100644
--- a/repoze/bfg/tests/test_configuration.py
+++ b/repoze/bfg/tests/test_configuration.py
@@ -290,6 +290,15 @@ class ConfiguratorTests(unittest.TestCase):
utility = reg.getUtility(IRequestFactory)
self.assertEqual(utility, 'abc')
+ def test_setup_registry_renderer_globals_factory(self):
+ from repoze.bfg.registry import Registry
+ from repoze.bfg.interfaces import IRendererGlobalsFactory
+ reg = Registry()
+ config = self._makeOne(reg)
+ config.setup_registry(renderer_globals_factory='abc')
+ utility = reg.getUtility(IRendererGlobalsFactory)
+ self.assertEqual(utility, 'abc')
+
def test_setup_registry_alternate_renderers(self):
from repoze.bfg.registry import Registry
from repoze.bfg.interfaces import IRendererFactory
@@ -300,6 +309,19 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(reg.getUtility(IRendererFactory, 'yeah'),
renderer)
+ def test_get_settings_nosettings(self):
+ from repoze.bfg.registry import Registry
+ reg = Registry()
+ config = self._makeOne(reg)
+ self.assertEqual(config.get_settings(), None)
+
+ def test_get_settings_withsettings(self):
+ from repoze.bfg.interfaces import ISettings
+ settings = {'a':1}
+ config = self._makeOne()
+ config.registry.registerUtility(settings, ISettings)
+ self.assertEqual(config.get_settings(), settings)
+
def test_add_settings_settings_already_registered(self):
from repoze.bfg.registry import Registry
from repoze.bfg.interfaces import ISettings
@@ -2462,7 +2484,52 @@ class ConfiguratorTests(unittest.TestCase):
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 repoze.bfg.testing import DummyTemplateRenderer
+ self.failUnless(isinstance(renderer, DummyTemplateRenderer))
+ from repoze.bfg.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 repoze.bfg.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 repoze.bfg.testing import DummyTemplateRenderer
+ self.failUnless(isinstance(renderer, DummyTemplateRenderer))
+ from repoze.bfg.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)
class Test__map_view(unittest.TestCase):
def setUp(self):
@@ -2778,97 +2845,6 @@ class Test__map_view(unittest.TestCase):
request = self._makeRequest()
self.assertEqual(result(None, request).body, 'Hello!')
-class Test_rendered_response(unittest.TestCase):
- def setUp(self):
- testing.setUp()
-
- def tearDown(self):
- testing.tearDown()
-
- def _callFUT(self, renderer, response, view=None,
- context=None, request=None, renderer_name=None):
- from repoze.bfg.configuration import rendered_response
- if request is None:
- request = DummyRequest()
- return rendered_response(renderer, response, view,
- context, request, renderer_name)
-
- def _makeRenderer(self):
- def renderer(*arg):
- return 'Hello!'
- return renderer
-
- def test_is_response(self):
- renderer = self._makeRenderer()
- response = DummyResponse()
- result = self._callFUT(renderer, response)
- self.assertEqual(result, response)
-
- def test_calls_renderer(self):
- renderer = self._makeRenderer()
- response = {'a':'1'}
- result = self._callFUT(renderer, response)
- self.assertEqual(result.body, 'Hello!')
-
- def test_with_content_type(self):
- renderer = self._makeRenderer()
- response = {'a':'1'}
- request = DummyRequest()
- attrs = {'response_content_type':'text/nonsense'}
- request.__dict__.update(attrs)
- result = self._callFUT(renderer, response, request=request)
- self.assertEqual(result.content_type, 'text/nonsense')
-
- def test_with_headerlist(self):
- renderer = self._makeRenderer()
- response = {'a':'1'}
- request = DummyRequest()
- attrs = {'response_headerlist':[('a', '1'), ('b', '2')]}
- request.__dict__.update(attrs)
- result = self._callFUT(renderer, response, request=request)
- self.assertEqual(result.headerlist,
- [('Content-Type', 'text/html; charset=UTF-8'),
- ('Content-Length', '6'),
- ('a', '1'),
- ('b', '2')])
-
- def test_with_status(self):
- renderer = self._makeRenderer()
- response = {'a':'1'}
- request = DummyRequest()
- attrs = {'response_status':'406 You Lose'}
- request.__dict__.update(attrs)
- result = self._callFUT(renderer, response, request=request)
- self.assertEqual(result.status, '406 You Lose')
-
- def test_with_charset(self):
- renderer = self._makeRenderer()
- response = {'a':'1'}
- request = DummyRequest()
- attrs = {'response_charset':'UTF-16'}
- request.__dict__.update(attrs)
- result = self._callFUT(renderer, response, request=request)
- self.assertEqual(result.charset, 'UTF-16')
-
- def test_with_cache_for(self):
- renderer = self._makeRenderer()
- response = {'a':'1'}
- request = DummyRequest()
- attrs = {'response_cache_for':100}
- request.__dict__.update(attrs)
- result = self._callFUT(renderer, response, request=request)
- self.assertEqual(result.cache_control.max_age, 100)
-
- def test_with_real_request(self):
- # functional
- from repoze.bfg.request import Request
- renderer = self._makeRenderer()
- response = {'a':'1'}
- request = Request({})
- request.response_status = '406 You Lose'
- result = self._callFUT(renderer, response, request=request)
- self.assertEqual(result.status, '406 You Lose')
-
class Test_decorate_view(unittest.TestCase):
def _callFUT(self, wrapped, original):
from repoze.bfg.configuration import decorate_view
diff --git a/repoze/bfg/tests/test_renderers.py b/repoze/bfg/tests/test_renderers.py
index 1f4df3556..8a8a578eb 100644
--- a/repoze/bfg/tests/test_renderers.py
+++ b/repoze/bfg/tests/test_renderers.py
@@ -120,21 +120,29 @@ class TestRendererFromName(unittest.TestCase):
def tearDown(self):
cleanUp()
- def _callFUT(self, path):
+ def _callFUT(self, path, package=None):
from repoze.bfg.renderers import renderer_from_name
- return renderer_from_name(path)
+ return renderer_from_name(path, package)
def test_it(self):
from repoze.bfg.interfaces import IRendererFactory
import os
here = os.path.dirname(os.path.abspath(__file__))
fixture = os.path.join(here, 'fixtures/minimal.pt')
- renderer = {}
def factory(path, **kw):
- return renderer
+ return path
testing.registerUtility(factory, IRendererFactory, name='.pt')
result = self._callFUT(fixture)
- self.assertEqual(result, renderer)
+ self.assertEqual(result, fixture)
+
+ def test_with_package(self):
+ from repoze.bfg.interfaces import IRendererFactory
+ def factory(path, **kw):
+ return path
+ testing.registerUtility(factory, IRendererFactory, name='.pt')
+ import repoze.bfg.tests
+ result = self._callFUT('fixtures/minimal.pt', repoze.bfg.tests)
+ self.assertEqual(result, 'repoze.bfg.tests:fixtures/minimal.pt')
def test_it_no_renderer(self):
self.assertRaises(ValueError, self._callFUT, 'foo')
@@ -199,6 +207,295 @@ class Test_string_renderer_factory(unittest.TestCase):
renderer(None, {'request':request})
self.assertEqual(request.response_content_type, 'text/mishmash')
+class Test_rendered_response(unittest.TestCase):
+ def setUp(self):
+ testing.setUp()
+ from zope.deprecation import __show__
+ __show__.off()
+
+ def tearDown(self):
+ testing.tearDown()
+ from zope.deprecation import __show__
+ __show__.on()
+
+ def _callFUT(self, renderer, response, view=None,
+ context=None, request=None, renderer_name=None):
+ from repoze.bfg.renderers import rendered_response
+ if request is None:
+ request = testing.DummyRequest()
+ return rendered_response(renderer, response, view,
+ context, request, renderer_name)
+
+ def _makeRenderer(self):
+ def renderer(*arg):
+ return 'Hello!'
+ return renderer
+
+ def test_is_response(self):
+ renderer = self._makeRenderer()
+ response = DummyResponse()
+ result = self._callFUT(renderer, response)
+ self.assertEqual(result, response)
+
+ def test_calls_renderer(self):
+ renderer = self._makeRenderer()
+ response = {'a':'1'}
+ result = self._callFUT(renderer, response)
+ self.assertEqual(result.body, 'Hello!')
+
+class Test__make_response(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _callFUT(self, request, result):
+ from repoze.bfg.renderers import _make_response
+ return _make_response(request, result)
+
+ def test_with_content_type(self):
+ request = testing.DummyRequest()
+ attrs = {'response_content_type':'text/nonsense'}
+ request.__dict__.update(attrs)
+ response = self._callFUT(request, 'abc')
+ self.assertEqual(response.content_type, 'text/nonsense')
+ self.assertEqual(response.body, 'abc')
+
+ def test_with_headerlist(self):
+ request = testing.DummyRequest()
+ attrs = {'response_headerlist':[('a', '1'), ('b', '2')]}
+ request.__dict__.update(attrs)
+ response = self._callFUT(request, 'abc')
+ self.assertEqual(response.headerlist,
+ [('Content-Type', 'text/html; charset=UTF-8'),
+ ('Content-Length', '3'),
+ ('a', '1'),
+ ('b', '2')])
+ self.assertEqual(response.body, 'abc')
+
+ def test_with_status(self):
+ request = testing.DummyRequest()
+ attrs = {'response_status':'406 You Lose'}
+ request.__dict__.update(attrs)
+ response = self._callFUT(request, 'abc')
+ self.assertEqual(response.status, '406 You Lose')
+ self.assertEqual(response.body, 'abc')
+
+ def test_with_charset(self):
+ request = testing.DummyRequest()
+ attrs = {'response_charset':'UTF-16'}
+ request.__dict__.update(attrs)
+ response = self._callFUT(request, 'abc')
+ self.assertEqual(response.charset, 'UTF-16')
+
+ def test_with_cache_for(self):
+ request = testing.DummyRequest()
+ attrs = {'response_cache_for':100}
+ request.__dict__.update(attrs)
+ response = self._callFUT(request, 'abc')
+ self.assertEqual(response.cache_control.max_age, 100)
+
+ def test_with_alternate_response_factory(self):
+ from repoze.bfg.interfaces import IResponseFactory
+ class ResponseFactory(object):
+ def __init__(self, result):
+ self.result = result
+ self.config.registry.registerUtility(ResponseFactory, IResponseFactory)
+ request = testing.DummyRequest()
+ response = self._callFUT(request, 'abc')
+ self.assertEqual(response.__class__, ResponseFactory)
+ self.assertEqual(response.result, 'abc')
+
+ def test_with_real_request(self):
+ # functional
+ from repoze.bfg.request import Request
+ request = Request({})
+ attrs = {'response_status':'406 You Lose'}
+ request.__dict__.update(attrs)
+ response = self._callFUT(request, 'abc')
+ self.assertEqual(response.status, '406 You Lose')
+ self.assertEqual(response.body, 'abc')
+
+class Test__render(unittest.TestCase):
+ def setUp(self):
+ self.config = cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
+ def _callFUT(self, renderer_name, request, values, system_values, renderer,
+ package):
+ from repoze.bfg.renderers import _render
+ return _render(renderer_name, request, values, system_values, renderer,
+ package)
+
+ def test_explicit_renderer(self):
+ def renderer(*arg):
+ return arg
+ result = self._callFUT(
+ 'name', 'request', 'values', 'system_values', renderer, None)
+ self.assertEqual(result, ('values', 'system_values'))
+
+ def test_request_has_registry(self):
+ request = Dummy()
+ class DummyRegistry(object):
+ def queryUtility(self, iface):
+ self.queried = True
+ return None
+ reg = DummyRegistry()
+ request.registry = reg
+ def renderer(*arg):
+ return arg
+ result = self._callFUT(
+ 'name', request, 'values', 'system_values', renderer, None)
+ self.assertEqual(result, ('values', 'system_values'))
+ self.failUnless(reg.queried)
+
+ def test_renderer_is_None(self):
+ from repoze.bfg.interfaces import IRendererFactory
+ def renderer(values, system):
+ return rf
+ def rf(spec):
+ return renderer
+ self.config.registry.registerUtility(rf, IRendererFactory, name='name')
+ result = self._callFUT(
+ 'name', 'request', 'values', 'system_values', None, None)
+ self.assertEqual(result, rf)
+
+ def test_system_values_is_None(self):
+ def renderer(*arg):
+ return arg
+ request = Dummy()
+ context = Dummy()
+ request.context = context
+ result = self._callFUT(
+ 'name', request, 'values', None, renderer, None)
+ system = {'request':request, 'context':context,
+ 'renderer_name':'name', 'view':None}
+ self.assertEqual(result, ('values', system))
+
+ def test_renderer_globals_factory_active(self):
+ from repoze.bfg.interfaces import IRendererGlobalsFactory
+ def rg(system):
+ return {'a':1}
+ self.config.registry.registerUtility(rg, IRendererGlobalsFactory)
+ def renderer(*arg):
+ return arg
+ result = self._callFUT(
+ 'name', 'request', 'values', {'a':2}, renderer, None)
+ self.assertEqual(result, ('values', {'a':1}))
+
+class Test__render_to_response(unittest.TestCase):
+ def setUp(self):
+ self.config = cleanUp()
+
+ def tearDown(self):
+ cleanUp()
+
+ def _callFUT(self, renderer_name, request, values, system_values, renderer,
+ package):
+ from repoze.bfg.renderers import _render_to_response
+ return _render_to_response(
+ renderer_name, request, values, system_values, renderer,
+ package)
+
+ def test_it(self):
+ def renderer(*arg):
+ return 'hello'
+ request = Dummy()
+ result = self._callFUT(
+ 'name', request, 'values', 'system_values', renderer, None)
+ self.assertEqual(result.body, 'hello')
+
+class Test_render(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _callFUT(self, renderer_name, **kw):
+ from repoze.bfg.renderers import render
+ return render(renderer_name, **kw)
+
+ def test_it_no_request(self):
+ renderer = self.config.testing_add_renderer(
+ 'repoze.bfg.tests:abc/def.pt')
+ renderer.string_response = 'abc'
+ result = self._callFUT('abc/def.pt', a=1)
+ self.assertEqual(result, 'abc')
+ renderer.assert_(a=1)
+ renderer.assert_(request=None)
+
+ def test_it_with_request(self):
+ renderer = self.config.testing_add_renderer(
+ 'repoze.bfg.tests:abc/def.pt')
+ renderer.string_response = 'abc'
+ request = testing.DummyRequest()
+ result = self._callFUT('abc/def.pt',
+ a=1, request=request)
+ self.assertEqual(result, 'abc')
+ renderer.assert_(a=1)
+ renderer.assert_(request=request)
+
+class Test_render_to_response(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _callFUT(self, renderer_name, **kw):
+ from repoze.bfg.renderers import render_to_response
+ return render_to_response(renderer_name, **kw)
+
+ def test_it_no_request(self):
+ renderer = self.config.testing_add_renderer(
+ 'repoze.bfg.tests:abc/def.pt')
+ renderer.string_response = 'abc'
+ response = self._callFUT('abc/def.pt', a=1)
+ self.assertEqual(response.body, 'abc')
+ renderer.assert_(a=1)
+ renderer.assert_(request=None)
+
+ def test_it_with_request(self):
+ renderer = self.config.testing_add_renderer(
+ 'repoze.bfg.tests:abc/def.pt')
+ renderer.string_response = 'abc'
+ request = testing.DummyRequest()
+ response = self._callFUT('abc/def.pt',
+ a=1, request=request)
+ self.assertEqual(response.body, 'abc')
+ renderer.assert_(a=1)
+ renderer.assert_(request=request)
+
+class Test_get_renderer(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def _callFUT(self, renderer_name, **kw):
+ from repoze.bfg.renderers import get_renderer
+ return get_renderer(renderer_name)
+
+ def test_it(self):
+ renderer = self.config.testing_add_renderer(
+ 'repoze.bfg.tests:abc/def.pt')
+ result = self._callFUT('abc/def.pt')
+ self.assertEqual(result, renderer)
+
+class Dummy:
+ pass
+
+class DummyResponse:
+ status = '200 OK'
+ headerlist = ()
+ app_iter = ()
+ body = ''
+
class DummyFactory:
def __init__(self, renderer):
self.renderer = renderer
diff --git a/repoze/bfg/tests/test_testing.py b/repoze/bfg/tests/test_testing.py
index 1b3d731fa..fc3bd7c9e 100644
--- a/repoze/bfg/tests/test_testing.py
+++ b/repoze/bfg/tests/test_testing.py
@@ -66,18 +66,18 @@ class Test_registerTemplateRenderer(TestBase):
renderer = testing.registerTemplateRenderer('templates/foo')
from repoze.bfg.testing import DummyTemplateRenderer
self.failUnless(isinstance(renderer, DummyTemplateRenderer))
- from repoze.bfg.chameleon_zpt import render_template_to_response
- render_template_to_response('templates/foo', foo=1, bar=2)
- self.assertEqual(dict(foo=1, bar=2), renderer._received)
+ from repoze.bfg.renderers import render_to_response
+ render_to_response('templates/foo', foo=1, bar=2)
+ renderer.assert_(foo=1)
+ renderer.assert_(bar=2)
def test_registerTemplateRenderer_explicitrenderer(self):
from repoze.bfg import testing
def renderer(kw, system):
- raise ValueError
+ self.assertEqual(kw, {'foo':1, 'bar':2})
renderer = testing.registerTemplateRenderer('templates/foo', renderer)
- from repoze.bfg.chameleon_zpt import render_template_to_response
- self.assertRaises(ValueError, render_template_to_response,
- 'templates/foo', foo=1, bar=2)
+ from repoze.bfg.renderers import render_to_response
+ render_to_response('templates/foo', foo=1, bar=2)
class Test_registerEventListener(TestBase):
def test_registerEventListener_single(self):
@@ -526,8 +526,8 @@ class TestDummyTemplateRenderer(unittest.TestCase):
renderer = self._makeOne()
impl = renderer.implementation()
impl(a=1, b=2)
- self.assertEqual(renderer._received['a'], 1)
- self.assertEqual(renderer._received['b'], 2)
+ self.assertEqual(renderer._implementation._received['a'], 1)
+ self.assertEqual(renderer._implementation._received['b'], 2)
def test_getattr(self):
renderer = self._makeOne()
@@ -666,6 +666,59 @@ class Test_tearDown(unittest.TestCase):
getSiteManager.reset()
manager.clear()
+class TestDummyRendererFactory(unittest.TestCase):
+ def _makeOne(self, name, factory):
+ from repoze.bfg.testing import DummyRendererFactory
+ return DummyRendererFactory(name, factory)
+
+ def test_add_no_colon(self):
+ f = self._makeOne('name', None)
+ f.add('spec', 'renderer')
+ self.assertEqual(f.renderers['spec'], 'renderer')
+
+ def test_add_with_colon(self):
+ f = self._makeOne('name', None)
+ f.add('spec:spec2', 'renderer')
+ self.assertEqual(f.renderers['spec:spec2'], 'renderer')
+ self.assertEqual(f.renderers['spec2'], 'renderer')
+
+ def test_call(self):
+ f = self._makeOne('name', None)
+ f.renderers['spec'] = 'renderer'
+ self.assertEqual(f('spec'), 'renderer')
+
+ def test_call2(self):
+ f = self._makeOne('name', None)
+ f.renderers['spec'] = 'renderer'
+ self.assertEqual(f('spec:spec'), 'renderer')
+
+ def test_call3(self):
+ def factory(spec):
+ return 'renderer'
+ f = self._makeOne('name', factory)
+ self.assertEqual(f('spec'), 'renderer')
+
+ def test_call_miss(self):
+ f = self._makeOne('name', None)
+ self.assertRaises(KeyError, f, 'spec')
+
+class TestMockTemplate(unittest.TestCase):
+ def _makeOne(self, response):
+ from repoze.bfg.testing import MockTemplate
+ return MockTemplate(response)
+
+ def test_getattr(self):
+ template = self._makeOne(None)
+ self.assertEqual(template.foo, template)
+
+ def test_getitem(self):
+ template = self._makeOne(None)
+ self.assertEqual(template['foo'], template)
+
+ def test_call(self):
+ template = self._makeOne('123')
+ self.assertEqual(template(), '123')
+
from zope.interface import Interface
from zope.interface import implements