diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | CHANGES.txt | 14 | ||||
| -rw-r--r-- | docs/narr/environment.rst | 2 | ||||
| -rw-r--r-- | docs/narr/views.rst | 54 | ||||
| -rw-r--r-- | pyramid/chameleon_zpt.py | 8 | ||||
| -rw-r--r-- | pyramid/configuration.py | 84 | ||||
| -rw-r--r-- | pyramid/mako_templating.py | 40 | ||||
| -rw-r--r-- | pyramid/renderers.py | 112 | ||||
| -rw-r--r-- | pyramid/testing.py | 5 | ||||
| -rw-r--r-- | pyramid/tests/test_configuration.py | 59 | ||||
| -rw-r--r-- | pyramid/tests/test_mako_templating.py | 60 | ||||
| -rw-r--r-- | pyramid/tests/test_renderers.py | 152 | ||||
| -rw-r--r-- | pyramid/tests/test_testing.py | 12 | ||||
| -rw-r--r-- | pyramid/tests/test_view.py | 31 | ||||
| -rw-r--r-- | pyramid/view.py | 20 | ||||
| -rw-r--r-- | pyramid/zcml.py | 5 |
16 files changed, 354 insertions, 305 deletions
diff --git a/.gitignore b/.gitignore index 31276c18f..92bc7bc23 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ .coverage env26 env24 +env27 build/ diff --git a/CHANGES.txt b/CHANGES.txt index dcfdbcaaa..8dbbbfd66 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -90,3 +90,17 @@ Backwards Incompatibilities (with BFG 1.3.X) - The Venusian "category" for all built-in Venusian decorators (e.g. ``subscriber`` and ``view_config``/``bfg_view``) is now ``pyramid`` instead of ``bfg``. + +- ``pyramid.renderers.rendered_response`` function removed; use + ``render_pyramid.renderers.render_to_response`` instead. + +- Renderer factories now accept a *dictionary* rather than an absolute resource + specification or an absolute path. The dictonary contains the following + keys: ``name`` (the ``renderer=`` value), ``package`` (the 'current package' + when the renderer configuration statement was found), ``type``: the renderer + type, and ``registry``: the current registry. + + Third-party ``repoze.bfg`` renderer implementations that must be ported to + Pyramid will need to account for this. + + This change was made to support more flexible Mako template rendering. diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst index fa639cce1..83de12863 100644 --- a/docs/narr/environment.rst +++ b/docs/narr/environment.rst @@ -195,7 +195,7 @@ should be changed accordingly. +-----------------------------+ | Config File Setting Name | +=============================+ -| ``mako.input_encoding``__ | +| ``mako.input_encoding`` | | | | | | | diff --git a/docs/narr/views.rst b/docs/narr/views.rst index c74263bdc..0070e7a73 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -738,9 +738,12 @@ following interface: :linenos: class RendererFactory: - def __init__(self, name): - """ Constructor: ``name`` may be an absolute path or a - resource specification """ + def __init__(self, info): + """ Constructor: ``info`` will be a dictionary containing the + following keys: ``name`` (the renderer name), ``package``: the + package that was 'current' at the time the renderer was registered, + ``type``: the renderer type name, and ``registry``: the current + registry. """ def __call__(self, value, system): """ Call a the renderer implementation with the value and @@ -752,18 +755,17 @@ following interface: There are essentially two different kinds of renderer factories: -- A renderer factory which expects to accept a :term:`resource - specification` or an absolute path as the ``name`` value in its - constructor. These renderer factories are registered with a - ``name`` value that begins with a dot (``.``). These types of - renderer factories usually relate to a file on the filesystem, such - as a template. +- A renderer factory which expects to accept a :term:`resource specification` + or an absolute path as the ``name`` value in the ``info`` value fed to its + constructor. These renderer factories are registered with a ``name`` value + that begins with a dot (``.``). These types of renderer factories usually + relate to a file on the filesystem, such as a template. -- A renderer factory which expects to accept a token that does not - represent a filesystem path or a resource specification in its - constructor. These renderer factories are registered with a - ``name`` value that does not begin with a dot. These renderer - factories are typically object serializers. +- A renderer factory which expects to accept a token that does not represent a + filesystem path or a resource specification in the ``name`` value of the + ``info`` dict fed to its constructor. These renderer factories are + registered with a ``name`` value that does not begin with a dot. These + renderer factories are typically object serializers. .. sidebar:: Resource Specifications @@ -832,19 +834,17 @@ configuration`: def myview(request): return {'Hello':'world'} -When a :term:`view configuration` which has a ``name`` attribute that -does contain a dot, such as ``templates/mytemplate.jinja2`` above is -encountered at startup time, the value of the name attribute is split -on its final dot. The second element of the split is typically the -filename extension. This extension is used to look up a renderer -factory for the configured view. Then the value of ``renderer`` is -passed to the factory to create a renderer for the view. In this -case, the view configuration will create an instance of a -``Jinja2Renderer`` for each view configuration which includes anything -ending with ``.jinja2`` as its ``renderer`` value. The ``name`` -passed to the ``Jinja2Renderer`` constructor will usually be a -:term:`resource specification`, but may also be an absolute path; the -renderer factory implementation should be able to deal with either. +When a :term:`view configuration` which has a ``name`` attribute that does +contain a dot, such as ``templates/mytemplate.jinja2`` above is encountered at +startup time, the value of the name attribute is split on its final dot. The +second element of the split is typically the filename extension. This +extension is used to look up a renderer factory for the configured view. Then +the value of ``renderer`` is passed to the factory to create a renderer for the +view. In this case, the view configuration will create an instance of a +``Jinja2Renderer`` for each view configuration which includes anything ending +with ``.jinja2`` as its ``renderer`` value. The ``name`` passed to the +``Jinja2Renderer`` constructor will be whatever the user passed as +``renderer=`` to the view configuration. See also :ref:`renderer_directive` and :meth:`pyramid.configuration.Configurator.add_renderer`. diff --git a/pyramid/chameleon_zpt.py b/pyramid/chameleon_zpt.py index a9671c411..ec25bded7 100644 --- a/pyramid/chameleon_zpt.py +++ b/pyramid/chameleon_zpt.py @@ -70,7 +70,7 @@ def get_renderer(path): :func:`pyramid.renderers.get_renderer` instead. """ package = caller_package() - factory = renderers.RendererHelper(path, package=package) + factory = renderers.RendererHelper(name=path, package=package) return factory.get_renderer() def get_template(path): @@ -84,7 +84,7 @@ def get_template(path): :func:`pyramid.renderers.get_renderer` instead. """ package = caller_package() - factory = renderers.RendererHelper(path, package=package) + factory = renderers.RendererHelper(name=path, package=package) return factory.get_renderer().implementation() def render_template(path, **kw): @@ -100,7 +100,7 @@ def render_template(path, **kw): """ package = caller_package() request = kw.pop('request', None) - renderer = renderers.RendererHelper(path, package=package) + renderer = renderers.RendererHelper(name=path, package=package) return renderer.render(kw, None, request=request) def render_template_to_response(path, **kw): @@ -117,5 +117,5 @@ def render_template_to_response(path, **kw): """ package = caller_package() request = kw.pop('request', None) - renderer = renderers.RendererHelper(path, package=package) + renderer = renderers.RendererHelper(name=path, package=package) return renderer.render_to_response(kw, None, request=request) diff --git a/pyramid/configuration.py b/pyramid/configuration.py index 03966c3fd..3c2e3b6b5 100644 --- a/pyramid/configuration.py +++ b/pyramid/configuration.py @@ -283,7 +283,7 @@ class Configurator(object): return resolve_resource_spec(path_or_spec, self.package_name) def _derive_view(self, view, permission=None, predicates=(), - attr=None, renderer_name=None, wrapper_viewname=None, + attr=None, renderer=None, wrapper_viewname=None, viewname=None, accept=None, order=MAX_ORDER, phash=DEFAULT_PHASH): view = self.maybe_dotted(view) @@ -291,8 +291,7 @@ class Configurator(object): authz_policy = self.registry.queryUtility(IAuthorizationPolicy) settings = self.registry.queryUtility(ISettings) logger = self.registry.queryUtility(IDebugLogger) - mapped_view = _map_view(view, attr, renderer_name, self.registry, - self.package) + mapped_view = _map_view(view, attr, renderer, self.registry) owrapped_view = _owrap_view(mapped_view, viewname, wrapper_viewname) secured_view = _secure_view(owrapped_view, permission, authn_policy, authz_policy) @@ -543,7 +542,9 @@ class Configurator(object): supplied, the user-supplied view must itself return a :term:`response` object. """ - return self._derive_view(view, attr=attr, renderer_name=renderer) + if renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + return self._derive_view(view, attr=attr, renderer=renderer) def add_subscriber(self, subscriber, iface=None, info=u''): """Add an event :term:`subscriber` for the event stream @@ -1072,6 +1073,9 @@ class Configurator(object): # intent: will be None if no default permission is registered permission = self.registry.queryUtility(IDefaultPermission) + if renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + # NO_PERMISSION_REQUIRED handled by _secure_view derived_view = self._derive_view(view, permission, predicates, attr, renderer, wrapper, name, accept, @@ -1687,7 +1691,9 @@ class Configurator(object): The ``wrapper`` argument should be the name of another view which will wrap this view when rendered (see the ``add_view`` method's ``wrapper`` argument for a description).""" - view = self._derive_view(view, attr=attr, renderer_name=renderer) + if renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + view = self._derive_view(view, attr=attr, renderer=renderer) def bwcompat_view(context, request): context = getattr(request, 'context', None) return view(context, request) @@ -1724,7 +1730,9 @@ class Configurator(object): which will wrap this view when rendered (see the ``add_view`` method's ``wrapper`` argument for a description). """ - view = self._derive_view(view, attr=attr, renderer_name=renderer) + if renderer is not None and not isinstance(renderer, dict): + renderer = {'name':renderer, 'package':self.package} + view = self._derive_view(view, attr=attr, renderer=renderer) def bwcompat_view(context, request): context = getattr(request, 'context', None) return view(context, request) @@ -2090,18 +2098,18 @@ class Configurator(object): """ from pyramid.testing import DummyRendererFactory - helper = RendererHelper(path, registry=self.registry) + helper = RendererHelper(name=path, registry=self.registry) factory = helper.factory if not isinstance(factory, DummyRendererFactory): - factory = DummyRendererFactory(helper.renderer_type, + factory = DummyRendererFactory(helper.type, helper.factory) self.registry.registerUtility(factory, IRendererFactory, - name=helper.renderer_type) + name=helper.type) from pyramid.testing import DummyTemplateRenderer if renderer is None: renderer = DummyTemplateRenderer() - factory.add(helper.renderer_name, renderer) + factory.add(helper.name, renderer) return renderer testing_add_template = testing_add_renderer @@ -2426,15 +2434,15 @@ def is_response(ob): return True return False -def _map_view(view, attr=None, renderer_name=None, registry=None, - package=None): +def _map_view(view, attr=None, renderer=None, registry=None): wrapped_view = view helper = None - if renderer_name: - helper = RendererHelper(renderer_name, - registry=registry, - package=package) + + if renderer is not None: + helper = RendererHelper(renderer['name'], + package=renderer['package'], + registry=registry) if inspect.isclass(view): # If the object we've located is a class, turn it into a @@ -2454,8 +2462,13 @@ def _map_view(view, attr=None, renderer_name=None, registry=None, response = getattr(inst, attr)() if helper is not None: if not is_response(response): - system = {'view':inst, 'renderer_name':renderer_name, - 'context':context, 'request':request} + system = { + 'view':inst, + 'renderer_name':renderer['name'], # b/c + 'renderer_info':renderer, + 'context':context, + 'request':request + } response = helper.render_to_response(response, system, request=request) return response @@ -2470,8 +2483,12 @@ def _map_view(view, attr=None, renderer_name=None, registry=None, response = getattr(inst, attr)() if helper is not None: if not is_response(response): - system = {'view':inst, 'renderer_name':renderer_name, - 'context':context, 'request':request} + system = {'view':inst, + 'renderer_name':renderer['name'], # b/c + 'renderer_info':renderer, + 'context':context, + 'request':request + } response = helper.render_to_response(response, system, request=request) return response @@ -2488,8 +2505,13 @@ def _map_view(view, attr=None, renderer_name=None, registry=None, if helper is not None: if not is_response(response): - system = {'view':view, 'renderer_name':renderer_name, - 'context':context, 'request':request} + system = { + 'view':view, + 'renderer_name':renderer['name'], + 'renderer_info':renderer, + 'context':context, + 'request':request + } response = helper.render_to_response(response, system, request=request) return response @@ -2500,8 +2522,13 @@ def _map_view(view, attr=None, renderer_name=None, registry=None, response = getattr(view, attr)(context, request) if helper is not None: if not is_response(response): - system = {'view':view, 'renderer_name':renderer_name, - 'context':context, 'request':request} + system = { + 'view':view, + 'renderer_name':renderer['name'], + 'renderer_info':renderer, + 'context':context, + 'request':request + } response = helper.render_to_response(response, system, request=request) return response @@ -2511,8 +2538,13 @@ def _map_view(view, attr=None, renderer_name=None, registry=None, def _rendered_view(context, request): response = view(context, request) if not is_response(response): - system = {'view':view, 'renderer_name':renderer_name, - 'context':context, 'request':request} + system = { + 'view':view, + 'renderer_name':renderer['name'], # b/c + 'renderer_info':renderer, + 'context':context, + 'request':request + } response = helper.render_to_response(response, system, request=request) return response diff --git a/pyramid/mako_templating.py b/pyramid/mako_templating.py index fb7a40c1c..ec6119764 100644 --- a/pyramid/mako_templating.py +++ b/pyramid/mako_templating.py @@ -1,13 +1,10 @@ import os -import posixpath -import re from zope.interface import implements from zope.interface import Interface from pyramid.interfaces import ITemplateRenderer from pyramid.exceptions import ConfigurationError -from pyramid.threadlocal import get_current_registry from pyramid.settings import get_settings from pyramid.resource import resolve_resource_spec from pyramid.resource import abspath_from_resource_spec @@ -39,31 +36,26 @@ class PkgResourceTemplateLookup(TemplateLookup): specification syntax. """ - if ':' not in uri: - return TemplateLookup.get_template(self, uri) - try: - if self.filesystem_checks: - return self._check(uri, self._collection[uri]) - else: - return self._collection[uri] - except KeyError: - pname, path = resolve_resource_spec(uri) - srcfile = abspath_from_resource_spec(path, pname) - if os.path.isfile(srcfile): - return self._load(srcfile, uri) - - u = re.sub(r'^\/+', '', path) - for dir in self.directories: - srcfile = posixpath.normpath(posixpath.join(dir, u)) + isabs = os.path.isabs(uri) + if (not isabs) and (':' in uri): + try: + if self.filesystem_checks: + return self._check(uri, self._collection[uri]) + else: + return self._collection[uri] + except KeyError: + pname, path = resolve_resource_spec(uri) + srcfile = abspath_from_resource_spec(path, pname) if os.path.isfile(srcfile): return self._load(srcfile, uri) - else: raise exceptions.TopLevelLookupException( - "Cant locate template for uri %r" % uri) + "Cant locate template for uri %r" % uri) + return TemplateLookup.get_template(self, uri) -def renderer_factory(path): - registry = get_current_registry() +def renderer_factory(info): + path = info['name'] + registry = info['registry'] lookup = registry.queryUtility(IMakoLookup) if lookup is None: settings = get_settings() or {} @@ -75,7 +67,7 @@ def renderer_factory(path): raise ConfigurationError( 'Mako template used without a lookup path') directories = directories.splitlines() - directories = [ abspath_from_resource_spec(d) for d in directories ] + directories = [ abspath_from_resource_spec(d) for d in directories ] lookup = PkgResourceTemplateLookup(directories=directories, module_directory=module_directory, input_encoding=input_encoding, diff --git a/pyramid/renderers.py b/pyramid/renderers.py index 251242916..78084ae97 100644 --- a/pyramid/renderers.py +++ b/pyramid/renderers.py @@ -4,19 +4,18 @@ import threading from webob import Response -from zope.deprecation import deprecated - from pyramid.interfaces import IRendererGlobalsFactory from pyramid.interfaces import IRendererFactory from pyramid.interfaces import IResponseFactory from pyramid.interfaces import ITemplateRenderer from pyramid.compat import json +from pyramid.decorator import reify from pyramid.path import caller_package +from pyramid.path import package_path +from pyramid.resource import resource_spec_from_abspath from pyramid.settings import get_settings from pyramid.threadlocal import get_current_registry -from pyramid.resource import resolve_resource_spec -from pyramid.decorator import reify # API @@ -60,7 +59,8 @@ def render(renderer_name, value, request=None, package=None): registry = None if package is None: package = caller_package() - renderer = RendererHelper(renderer_name, package=package, registry=registry) + renderer = RendererHelper(name=renderer_name, package=package, + registry=registry) return renderer.render(value, None, request=request) def render_to_response(renderer_name, value, request=None, package=None): @@ -102,7 +102,8 @@ def render_to_response(renderer_name, value, request=None, package=None): registry = None if package is None: package = caller_package() - renderer = RendererHelper(renderer_name, package=package, registry=registry) + renderer = RendererHelper(name=renderer_name, package=package, + registry=registry) return renderer.render_to_response(value, None, request=request) def get_renderer(renderer_name, package=None): @@ -119,13 +120,13 @@ def get_renderer(renderer_name, package=None): """ if package is None: package = caller_package() - renderer = RendererHelper(renderer_name, package=package) + renderer = RendererHelper(name=renderer_name, package=package) return renderer.get_renderer() # concrete renderer factory implementations (also API) -def json_renderer_factory(name): +def json_renderer_factory(info): def _render(value, system): request = system.get('request') if request is not None: @@ -134,7 +135,7 @@ def json_renderer_factory(name): return json.dumps(value) return _render -def string_renderer_factory(name): +def string_renderer_factory(info): def _render(value, system): if not isinstance(value, basestring): value = str(value) @@ -152,8 +153,19 @@ def string_renderer_factory(name): # at runtime, from more than a single thread. registry_lock = threading.Lock() -def template_renderer_factory(spec, impl, lock=registry_lock): - reg = get_current_registry() +def template_renderer_factory(info, impl, lock=registry_lock): + reg = info['registry'] + spec = info['name'] + package = info['package'] + isabs = os.path.isabs(spec) + + if (not isabs) and (not ':' in spec) and package: + # relative resource spec + if not isabs: + pp = package_path(package) + spec = os.path.join(pp, spec) + spec = resource_spec_from_abspath(spec, package) + if os.path.isabs(spec): # 'spec' is an absolute filename if not os.path.exists(spec): @@ -197,67 +209,39 @@ def _reload_resources(): settings = get_settings() return settings and settings.get('reload_resources') -def renderer_from_name(path, package=None): - return RendererHelper(path, package=package).get_renderer() - -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} - - helper = RendererHelper(renderer_name) - helper.renderer = renderer - return helper.render_to_response(result, system, request=request) - -deprecated('rendered_response', - "('pyramid.renderers.rendered_response' is not a public API; it is " - "officially deprecated as of pyramid 1.0; " - "use pyramid.renderers.render_to_response instead')", - ) +def renderer_from_name(path, package=None): # XXX deprecate? + return RendererHelper(name=path, package=package).get_renderer() class RendererHelper(object): - def __init__(self, renderer_name, registry=None, package=None): + def __init__(self, name=None, package=None, registry=None): if registry is None: registry = get_current_registry() - self.registry = registry - self.package = package - if renderer_name is None: - factory = registry.queryUtility(IRendererFactory) - renderer_type = None + + if name and '.' in name: + rtype = os.path.splitext(name)[1] else: - if '.' in renderer_name: - renderer_type = os.path.splitext(renderer_name)[1] - renderer_name = self.resolve_spec(renderer_name) - else: - renderer_type = renderer_name - renderer_name = renderer_name - factory = registry.queryUtility(IRendererFactory, - name=renderer_type) - self.renderer_name = renderer_name - self.renderer_type = renderer_type + rtype = name + + factory = registry.queryUtility(IRendererFactory, name=rtype) + + self.name = name + self.package = package + self.type = rtype self.factory = factory + self.registry = registry @reify def renderer(self): if self.factory is None: - raise ValueError('No such renderer factory %s' % str(self.renderer_type)) - return self.factory(self.renderer_name) - - def resolve_spec(self, path_or_spec): - if path_or_spec is None: - return path_or_spec - - package, filename = resolve_resource_spec(path_or_spec, - self.package) - if package is None: - return filename # absolute filename - return '%s:%s' % (package, filename) - + raise ValueError( + 'No such renderer factory %s' % str(self.type)) + return self.factory({ + 'name':self.name, + 'type':self.type, + 'package':self.package, + 'registry':self.registry + }) + def get_renderer(self): return self.renderer @@ -266,7 +250,9 @@ class RendererHelper(object): if system_values is None: system_values = { 'view':None, - 'renderer_name':self.renderer_name, + 'renderer_name':self.name, + 'renderer_type':self.type, + 'renderer_package':self.package, 'context':getattr(request, 'context', None), 'request':request, } diff --git a/pyramid/testing.py b/pyramid/testing.py index 188485635..26873680e 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -710,7 +710,8 @@ class DummyRendererFactory(object): package, relative = spec.split(':', 1) self.renderers[relative] = renderer - def __call__(self, spec): + def __call__(self, info): + spec = info['name'] renderer = self.renderers.get(spec) if renderer is None: if ':' in spec: @@ -718,7 +719,7 @@ class DummyRendererFactory(object): renderer = self.renderers.get(relative) if renderer is None: if self.factory: - renderer = self.factory(spec) + renderer = self.factory(info) else: raise KeyError('No testing renderer registered for %r' % spec) diff --git a/pyramid/tests/test_configuration.py b/pyramid/tests/test_configuration.py index e24876744..415d760ed 100644 --- a/pyramid/tests/test_configuration.py +++ b/pyramid/tests/test_configuration.py @@ -13,8 +13,8 @@ class ConfiguratorTests(unittest.TestCase): from zope.interface import implements class Renderer: implements(ITemplateRenderer) - def __init__(self, path): - self.__class__.path = path + def __init__(self, info): + self.__class__.info = info def __call__(self, *arg): return 'Hello!' config.registry.registerUtility(Renderer, IRendererFactory, name=name) @@ -1353,6 +1353,7 @@ class ConfiguratorTests(unittest.TestCase): self.assertEqual(wrapper(ctx, request), 'view8') def test_add_view_with_template_renderer(self): + import pyramid.tests class view(object): def __init__(self, context, request): self.request = request @@ -1368,9 +1369,13 @@ class ConfiguratorTests(unittest.TestCase): request = self._makeRequest(config) result = wrapper(None, request) self.assertEqual(result.body, 'Hello!') - self.assertEqual(renderer.path, 'pyramid.tests:fixtures/minimal.txt') + self.assertEqual(renderer.info, + {'registry':config.registry, 'type': '.txt', + 'name': 'pyramid.tests:fixtures/minimal.txt', + 'package': pyramid.tests}) def test_add_view_with_template_renderer_no_callable(self): + import pyramid.tests config = self._makeOne() renderer = self._registerRenderer(config) fixture = 'pyramid.tests:fixtures/minimal.txt' @@ -1379,7 +1384,12 @@ class ConfiguratorTests(unittest.TestCase): request = self._makeRequest(config) result = wrapper(None, request) self.assertEqual(result.body, 'Hello!') - self.assertEqual(renderer.path, 'pyramid.tests:fixtures/minimal.txt') + self.assertEqual(renderer.info, + {'registry':config.registry, + 'type': '.txt', + 'name': 'pyramid.tests:fixtures/minimal.txt', + 'package':pyramid.tests} + ) def test_add_view_with_request_type_as_iface(self): from zope.interface import directlyProvides @@ -3257,8 +3267,8 @@ class Test__map_view(unittest.TestCase): def test__map_view_as_function_with_attr_and_renderer(self): renderer = self._registerRenderer() view = lambda *arg: 'OK' - result = self._callFUT(view, attr='__name__', - renderer_name=renderer.spec) + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='__name__', renderer=info) self.failIf(result is view) self.assertRaises(TypeError, result, None, None) @@ -3316,7 +3326,8 @@ class Test__map_view(unittest.TestCase): pass def index(self): return {'a':'1'} - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) @@ -3357,7 +3368,8 @@ class Test__map_view(unittest.TestCase): pass def index(self): return {'a':'1'} - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) @@ -3398,7 +3410,8 @@ class Test__map_view(unittest.TestCase): pass def index(self): return {'a':'1'} - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) @@ -3439,7 +3452,8 @@ class Test__map_view(unittest.TestCase): pass def index(self): return {'a':'1'} - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) @@ -3471,7 +3485,8 @@ class Test__map_view(unittest.TestCase): def index(self, context, request): return {'a':'1'} view = View() - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) self.failIf(result is view) request = self._makeRequest() self.assertEqual(result(None, request).body, 'Hello!') @@ -3506,7 +3521,8 @@ class Test__map_view(unittest.TestCase): def index(self, request): return {'a':'1'} view = View() - result = self._callFUT(view, attr='index', renderer_name=renderer.spec) + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, attr='index', renderer=info) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) @@ -3518,7 +3534,8 @@ class Test__map_view(unittest.TestCase): renderer = self._registerRenderer() def view(context, request): return {'a':'1'} - result = self._callFUT(view, renderer_name=renderer.spec) + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, renderer=info) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) @@ -3529,7 +3546,8 @@ class Test__map_view(unittest.TestCase): renderer = self._registerRenderer() def view(context, request): return {'a':'1'} - result = self._callFUT(view, renderer_name=renderer.spec, + info = {'name':renderer.spec, 'package':None} + result = self._callFUT(view, renderer=info, registry=self.registry) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) @@ -3537,19 +3555,6 @@ class Test__map_view(unittest.TestCase): request = self._makeRequest() self.assertEqual(result(None, request).body, 'Hello!') - def test__map_view_with_package(self): - renderer = self._registerRenderer() - def view(context, request): - return {'a':'1'} - result = self._callFUT(view, renderer_name=renderer.spec, - package='pyramid') - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - self.assertEqual(renderer.path, 'pyramid:abc.txt') - class Test_decorate_view(unittest.TestCase): def _callFUT(self, wrapped, original): from pyramid.configuration import decorate_view diff --git a/pyramid/tests/test_mako_templating.py b/pyramid/tests/test_mako_templating.py index 54df83a79..37780d1ba 100644 --- a/pyramid/tests/test_mako_templating.py +++ b/pyramid/tests/test_mako_templating.py @@ -15,18 +15,22 @@ class Base(object): self.config.end() class Test_renderer_factory(Base, unittest.TestCase): - def _callFUT(self, path): + def _callFUT(self, info): from pyramid.mako_templating import renderer_factory - return renderer_factory(path) + return renderer_factory(info) def test_no_directories(self): from pyramid.exceptions import ConfigurationError - self.assertRaises(ConfigurationError, self._callFUT, 'path') + info = {'name':'helloworld.mak', 'package':None, + 'registry':self.config.registry} + self.assertRaises(ConfigurationError, self._callFUT, info) def test_no_lookup(self): from pyramid.mako_templating import IMakoLookup self.config.add_settings({'mako.directories':self.templates_dir}) - renderer = self._callFUT('helloworld.mak') + info = {'name':'helloworld.mak', 'package':None, + 'registry':self.config.registry} + renderer = self._callFUT(info) lookup = self.config.registry.getUtility(IMakoLookup) self.assertEqual(lookup.directories, [self.templates_dir]) self.assertEqual(lookup.filesystem_checks, False) @@ -37,7 +41,9 @@ class Test_renderer_factory(Base, unittest.TestCase): from pyramid.mako_templating import IMakoLookup twice = self.templates_dir + '\n' + self.templates_dir self.config.add_settings({'mako.directories':twice}) - self._callFUT('helloworld.mak') + info = {'name':'helloworld.mak', 'package':None, + 'registry':self.config.registry} + self._callFUT(info) lookup = self.config.registry.getUtility(IMakoLookup) self.assertEqual(lookup.directories, [self.templates_dir]*2) @@ -45,7 +51,9 @@ class Test_renderer_factory(Base, unittest.TestCase): from pyramid.mako_templating import IMakoLookup lookup = dict() self.config.registry.registerUtility(lookup, IMakoLookup) - renderer = self._callFUT('helloworld.mak') + info = {'name':'helloworld.mak', 'package':None, + 'registry':self.config.registry} + renderer = self._callFUT(info) self.assertEqual(renderer.lookup, lookup) self.assertEqual(renderer.path, 'helloworld.mak') @@ -163,8 +171,46 @@ class TestIntegration(unittest.TestCase): def test_template_not_found(self): from pyramid.renderers import render from mako.exceptions import TemplateLookupException - self.assertRaises(TemplateLookupException, render, 'helloworld_not_here.mak', {}) + self.assertRaises(TemplateLookupException, render, + 'helloworld_not_here.mak', {}) +class TestPkgResourceTemplateLookup(unittest.TestCase): + def _makeOne(self, **kw): + from pyramid.mako_templating import PkgResourceTemplateLookup + return PkgResourceTemplateLookup(**kw) + + def get_fixturedir(self): + import os + import pyramid.tests + return os.path.join(os.path.dirname(pyramid.tests.__file__), 'fixtures') + + def test_adjust_uri_not_resource_spec(self): + inst = self._makeOne() + result = inst.adjust_uri('a', None) + self.assertEqual(result, '/a') + + def test_adjust_uri_resource_spec(self): + inst = self._makeOne() + result = inst.adjust_uri('a:b', None) + self.assertEqual(result, 'a:b') + + def test_get_template_not_resource_spec(self): + fixturedir = self.get_fixturedir() + inst = self._makeOne(directories=[fixturedir]) + result = inst.get_template('helloworld.mak') + self.failIf(result is None) + + def test_get_template_resource_spec_with_filesystem_checks(self): + inst = self._makeOne(filesystem_checks=True) + result = inst.get_template('pyramid.tests:fixtures/helloworld.mak') + self.failIf(result is None) + + def test_get_template_resource_spec_missing(self): + from mako.exceptions import TopLevelLookupException + fixturedir = self.get_fixturedir() + inst = self._makeOne(filesystem_checks=True, directories=[fixturedir]) + self.assertRaises(TopLevelLookupException, inst.get_template, + 'pyramid.tests:fixtures/notthere.mak') class DummyLookup(object): def get_template(self, path): diff --git a/pyramid/tests/test_renderers.py b/pyramid/tests/test_renderers.py index 7f4d893e8..917bb7c40 100644 --- a/pyramid/tests/test_renderers.py +++ b/pyramid/tests/test_renderers.py @@ -5,7 +5,7 @@ from pyramid import testing class TestTemplateRendererFactory(unittest.TestCase): def setUp(self): - cleanUp() + self.config = cleanUp() def tearDown(self): cleanUp() @@ -18,7 +18,9 @@ class TestTemplateRendererFactory(unittest.TestCase): from pyramid.interfaces import ITemplateRenderer abspath = '/wont/exist' testing.registerUtility({}, ITemplateRenderer, name=abspath) - self.assertRaises(ValueError, self._callFUT, abspath, None) + info = {'name':abspath, 'package':None, + 'registry':self.config.registry} + self.assertRaises(ValueError, self._callFUT, info, None) def test_abspath_alreadyregistered(self): from pyramid.interfaces import ITemplateRenderer @@ -26,7 +28,9 @@ class TestTemplateRendererFactory(unittest.TestCase): abspath = os.path.abspath(__file__) renderer = {} testing.registerUtility(renderer, ITemplateRenderer, name=abspath) - result = self._callFUT(abspath, None) + info = {'name':abspath, 'package':None, + 'registry':self.config.registry} + result = self._callFUT(info, None) self.failUnless(result is renderer) def test_abspath_notyetregistered(self): @@ -35,26 +39,37 @@ class TestTemplateRendererFactory(unittest.TestCase): abspath = os.path.abspath(__file__) renderer = {} testing.registerUtility(renderer, ITemplateRenderer, name=abspath) - result = self._callFUT(abspath, None) + info = {'name':abspath, 'package':None, + 'registry':self.config.registry} + result = self._callFUT(info, None) self.failUnless(result is renderer) def test_relpath_path_registered(self): renderer = {} from pyramid.interfaces import ITemplateRenderer testing.registerUtility(renderer, ITemplateRenderer, name='foo/bar') - result = self._callFUT('foo/bar', None) + spec = 'foo/bar' + info = {'name':spec, 'package':None, + 'registry':self.config.registry} + result = self._callFUT(info, None) self.failUnless(renderer is result) - def test_relpath_is_package_registered(self): + def test_relpath_has_package_registered(self): renderer = {} from pyramid.interfaces import ITemplateRenderer - testing.registerUtility(renderer, ITemplateRenderer, name='foo:bar/baz') - result = self._callFUT('foo:bar/baz', None) + import pyramid.tests + spec = 'bar/baz' + testing.registerUtility(renderer, ITemplateRenderer, + name='pyramid.tests:bar/baz') + info = {'name':spec, 'package':pyramid.tests, + 'registry':self.config.registry} + result = self._callFUT(info, None) self.failUnless(renderer is result) def test_spec_notfound(self): - self.assertRaises(ValueError, self._callFUT, - 'pyramid.tests:wont/exist', None) + spec = 'pyramid.tests:wont/exist' + info = {'name':spec, 'package':None, 'registry':self.config.registry} + self.assertRaises(ValueError, self._callFUT, info, None) def test_spec_alreadyregistered(self): from pyramid.interfaces import ITemplateRenderer @@ -62,9 +77,10 @@ class TestTemplateRendererFactory(unittest.TestCase): module_name = tests.__name__ relpath = 'test_renderers.py' spec = '%s:%s' % (module_name, relpath) + info = {'name':spec, 'package':None, 'registry':self.config.registry} renderer = {} testing.registerUtility(renderer, ITemplateRenderer, name=spec) - result = self._callFUT(spec, None) + result = self._callFUT(info, None) self.failUnless(result is renderer) def test_spec_notyetregistered(self): @@ -75,7 +91,8 @@ class TestTemplateRendererFactory(unittest.TestCase): renderer = {} factory = DummyFactory(renderer) spec = '%s:%s' % (module_name, relpath) - result = self._callFUT(spec, factory) + info = {'name':spec, 'package':None, 'registry':self.config.registry} + result = self._callFUT(info, factory) self.failUnless(result is renderer) path = os.path.abspath(__file__) if path.endswith('pyc'): # pragma: no cover @@ -84,6 +101,7 @@ class TestTemplateRendererFactory(unittest.TestCase): self.assertEqual(factory.kw, {}) def test_reload_resources_true(self): + import pyramid.tests from pyramid.threadlocal import get_current_registry from pyramid.interfaces import ISettings from pyramid.interfaces import ITemplateRenderer @@ -91,7 +109,10 @@ class TestTemplateRendererFactory(unittest.TestCase): testing.registerUtility(settings, ISettings) renderer = {} factory = DummyFactory(renderer) - result = self._callFUT('pyramid.tests:test_renderers.py', factory) + spec = 'test_renderers.py' + info = {'name':spec, 'package':pyramid.tests, + 'registry':self.config.registry} + result = self._callFUT(info, factory) self.failUnless(result is renderer) spec = '%s:%s' % ('pyramid.tests', 'test_renderers.py') reg = get_current_registry() @@ -99,6 +120,7 @@ class TestTemplateRendererFactory(unittest.TestCase): None) def test_reload_resources_false(self): + import pyramid.tests from pyramid.threadlocal import get_current_registry from pyramid.interfaces import ISettings from pyramid.interfaces import ITemplateRenderer @@ -106,7 +128,10 @@ class TestTemplateRendererFactory(unittest.TestCase): testing.registerUtility(settings, ISettings) renderer = {} factory = DummyFactory(renderer) - result = self._callFUT('pyramid.tests:test_renderers.py', factory) + spec = 'test_renderers.py' + info = {'name':spec, 'package':pyramid.tests, + 'registry':self.config.registry} + result = self._callFUT(info, factory) self.failUnless(result is renderer) spec = '%s:%s' % ('pyramid.tests', 'test_renderers.py') reg = get_current_registry() @@ -125,24 +150,39 @@ class TestRendererFromName(unittest.TestCase): return renderer_from_name(path, package) def test_it(self): + from pyramid.threadlocal import get_current_registry + registry = get_current_registry() from pyramid.interfaces import IRendererFactory import os here = os.path.dirname(os.path.abspath(__file__)) fixture = os.path.join(here, 'fixtures/minimal.pt') - def factory(path, **kw): - return path + def factory(info, **kw): + return info testing.registerUtility(factory, IRendererFactory, name='.pt') result = self._callFUT(fixture) - self.assertEqual(result, fixture) - - def test_with_package(self): + self.assertEqual(result, {'registry':registry, + 'type':'.pt', + 'package':None, + 'name':fixture, + }) + + def test_it_with_package(self): + import pyramid + from pyramid.threadlocal import get_current_registry + registry = get_current_registry() from pyramid.interfaces import IRendererFactory - def factory(path, **kw): - return path + import os + here = os.path.dirname(os.path.abspath(__file__)) + fixture = os.path.join(here, 'fixtures/minimal.pt') + def factory(info, **kw): + return info testing.registerUtility(factory, IRendererFactory, name='.pt') - import pyramid.tests - result = self._callFUT('fixtures/minimal.pt', pyramid.tests) - self.assertEqual(result, 'pyramid.tests:fixtures/minimal.pt') + result = self._callFUT(fixture, pyramid) + self.assertEqual(result, {'registry':registry, + 'type':'.pt', + 'package':pyramid, + 'name':fixture, + }) def test_it_no_renderer(self): self.assertRaises(ValueError, self._callFUT, 'foo') @@ -207,42 +247,6 @@ 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 pyramid.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 TestRendererHelper(unittest.TestCase): def setUp(self): @@ -265,26 +269,6 @@ class TestRendererHelper(unittest.TestCase): name='.foo') return renderer - def test_resolve_spec_path_is_None(self): - helper = self._makeOne('loo.foo') - result = helper.resolve_spec(None) - self.assertEqual(result, None) - - def test_resolve_spec_package_is_None(self): - helper = self._makeOne('loo.foo') - result = helper.resolve_spec('/foo/bar') - self.assertEqual(result, '/foo/bar') - - def test_resolve_spec_absolute(self): - helper = self._makeOne('loo.foo') - result = helper.resolve_spec('pyramid:flub') - self.assertEqual(result, 'pyramid:flub') - - def test_resolve_spec_relative(self): - helper = self._makeOne('loo.foo', package='pyramid') - result = helper.resolve_spec('flub') - self.assertEqual(result, 'pyramid:flub') - def test_render_to_response(self): self._registerRendererFactory() request = Dummy() @@ -314,8 +298,12 @@ class TestRendererHelper(unittest.TestCase): request.context = context helper = self._makeOne('loo.foo') result = helper.render('values', None, request=request) - system = {'request':request, 'context':context, - 'renderer_name':'loo.foo', 'view':None} + system = {'request':request, + 'context':context, + 'renderer_name':'loo.foo', + 'view':None, + 'renderer_type':'.foo', + 'renderer_package':None} self.assertEqual(result, ('values', system)) def test_render_renderer_globals_factory_active(self): diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py index 05f9ad518..5929ce650 100644 --- a/pyramid/tests/test_testing.py +++ b/pyramid/tests/test_testing.py @@ -655,22 +655,26 @@ class TestDummyRendererFactory(unittest.TestCase): def test_call(self): f = self._makeOne('name', None) f.renderers['spec'] = 'renderer' - self.assertEqual(f('spec'), 'renderer') + info = {'name':'spec'} + self.assertEqual(f(info), 'renderer') def test_call2(self): f = self._makeOne('name', None) f.renderers['spec'] = 'renderer' - self.assertEqual(f('spec:spec'), 'renderer') + info = {'name':'spec:spec'} + self.assertEqual(f(info), 'renderer') def test_call3(self): def factory(spec): return 'renderer' f = self._makeOne('name', factory) - self.assertEqual(f('spec'), 'renderer') + info = {'name':'spec'} + self.assertEqual(f(info), 'renderer') def test_call_miss(self): f = self._makeOne('name', None) - self.assertRaises(KeyError, f, 'spec') + info = {'name':'spec'} + self.assertRaises(KeyError, f, info) class TestMockTemplate(unittest.TestCase): def _makeOne(self, response): diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py index fc725379e..64c2d9518 100644 --- a/pyramid/tests/test_view.py +++ b/pyramid/tests/test_view.py @@ -306,18 +306,8 @@ class TestViewConfigDecorator(unittest.TestCase): settings = call_venusian(venusian) self.assertEqual(settings[0]['custom_predicates'], (1,)) - def test_call_with_renderer_nodot(self): - decorator = self._makeOne(renderer='json') - venusian = DummyVenusian() - decorator.venusian = venusian - def foo(): pass - wrapped = decorator(foo) - self.failUnless(wrapped is foo) - settings = call_venusian(venusian) - self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['renderer'], 'json') - - def test_call_with_renderer_relpath(self): + def test_call_with_renderer_string(self): + import pyramid.tests decorator = self._makeOne(renderer='fixtures/minimal.pt') venusian = DummyVenusian() decorator.venusian = venusian @@ -326,12 +316,14 @@ class TestViewConfigDecorator(unittest.TestCase): self.failUnless(wrapped is foo) settings = call_venusian(venusian) self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['renderer'], - 'pyramid.tests:fixtures/minimal.pt') - - def test_call_with_renderer_pkgpath(self): - decorator = self._makeOne( - renderer='pyramid.tests:fixtures/minimal.pt') + renderer = settings[0]['renderer'] + self.assertEqual(renderer, + {'name':'fixtures/minimal.pt', + 'package':pyramid.tests, + }) + + def test_call_with_renderer_dict(self): + decorator = self._makeOne(renderer={'a':1}) venusian = DummyVenusian() decorator.venusian = venusian def foo(): pass @@ -339,8 +331,7 @@ class TestViewConfigDecorator(unittest.TestCase): self.failUnless(wrapped is foo) settings = call_venusian(venusian) self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['renderer'], - 'pyramid.tests:fixtures/minimal.pt') + self.assertEqual(settings[0]['renderer'], {'a':1}) class Test_append_slash_notfound_view(BaseTest, unittest.TestCase): def _callFUT(self, context, request): diff --git a/pyramid/view.py b/pyramid/view.py index 68a8b92e7..4b1eb94ed 100644 --- a/pyramid/view.py +++ b/pyramid/view.py @@ -1,5 +1,4 @@ import mimetypes -import os # See http://bugs.python.org/issue5853 which is a recursion bug # that seems to effect Python 2.6, Python 2.6.1, and 2.6.2 (a fix @@ -21,8 +20,6 @@ from pyramid.interfaces import IRoutesMapper from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier -from pyramid.path import package_path -from pyramid.resource import resource_spec_from_abspath from pyramid.static import static_view as static # B/C from pyramid.threadlocal import get_current_registry @@ -417,19 +414,10 @@ class view_config(object): if settings['attr'] is None: settings['attr'] = wrapped.__name__ - # try to convert the renderer provided into a fully qualified - # resource specification - abspath = settings.get('renderer') - if abspath is not None and '.' in abspath: - isabs = os.path.isabs(abspath) - if not (':' in abspath and not isabs): - # not already a resource spec - if not isabs: - pp = package_path(info.module) - abspath = os.path.join(pp, abspath) - resource = resource_spec_from_abspath(abspath, info.module) - settings['renderer'] = resource - + renderer_name = settings.get('renderer') + if renderer_name is not None and not isinstance(renderer_name, dict): + settings['renderer'] = {'name':renderer_name, + 'package':info.module} return wrapped bfg_view = view_config # permanent b/c diff --git a/pyramid/zcml.py b/pyramid/zcml.py index 80f73a3a2..5382deb99 100644 --- a/pyramid/zcml.py +++ b/pyramid/zcml.py @@ -188,8 +188,9 @@ def view( raise ConfigurationError( 'request_type must be an interface, not %s' % request_type) - if renderer and '.' in renderer: - renderer = path_spec(_context, renderer) + if renderer is not None: + package = getattr(_context, 'package', None) + renderer = {'name':renderer, 'package':package} context = context or for_ |
