Chris McDonough
2010-02-04 89968dce0273f49cf34e07480077f7843593f54e
- Fix a bug whereby a ``renderer`` argument to the ``@bfg_view``
decorator that provided a package-relative template filename might
not have been resolved properly. Symptom: inappropriate ``Missing
template resource`` errors.


5 files added
8 files modified
135 ■■■■ changed files
CHANGES.txt 5 ●●●●● patch | view | raw | blame | history
repoze/bfg/path.py 1 ●●●● patch | view | raw | blame | history
repoze/bfg/resource.py 16 ●●●●● patch | view | raw | blame | history
repoze/bfg/tests/restbugapp/views.py 3 ●●●●● patch | view | raw | blame | history
repoze/bfg/tests/test_integration.py 17 ●●●●● patch | view | raw | blame | history
repoze/bfg/tests/test_view.py 30 ●●●●● patch | view | raw | blame | history
repoze/bfg/tests/viewdecoratorapp/__init__.py 1 ●●●● patch | view | raw | blame | history
repoze/bfg/tests/viewdecoratorapp/configure.zcml 6 ●●●●● patch | view | raw | blame | history
repoze/bfg/tests/viewdecoratorapp/views/__init__.py 1 ●●●● patch | view | raw | blame | history
repoze/bfg/tests/viewdecoratorapp/views/templates/foo.pt 3 ●●●●● patch | view | raw | blame | history
repoze/bfg/tests/viewdecoratorapp/views/views.py 17 ●●●●● patch | view | raw | blame | history
repoze/bfg/view.py 17 ●●●●● patch | view | raw | blame | history
repoze/bfg/zcml.py 18 ●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -10,6 +10,11 @@
  class was being called instead of the method named via
  ``view_attr``.
- Fix a bug whereby a ``renderer`` argument to the ``@bfg_view``
  decorator that provided a package-relative template filename might
  not have been resolved properly.  Symptom: inappropriate ``Missing
  template resource`` errors.
1.2b4 (2010-02-03)
==================
repoze/bfg/path.py
@@ -56,4 +56,3 @@
            pass
    return prefix
repoze/bfg/resource.py
@@ -5,6 +5,7 @@
from repoze.bfg.interfaces import IPackageOverrides
from repoze.bfg.path import package_path
from repoze.bfg.threadlocal import get_current_registry
class OverrideProvider(pkg_resources.DefaultProvider):
@@ -180,3 +181,18 @@
    elif package_name is None:
        package_name, filename = None, spec
    return package_name, filename
def resource_spec_from_abspath(abspath, package):
    """ Try to convert an absolute path to a resource in a package to
    a resource specification if possible; otherwise return the
    absolute path.  """
    if getattr(package, '__name__', None) == '__main__':
        return abspath
    pp = package_path(package) + os.path.sep
    if abspath.startswith(pp):
        relpath = abspath[len(pp):]
        return '%s:%s' % (package.__name__,
                          relpath.replace(os.path.sep, '/'))
    return abspath
repoze/bfg/tests/restbugapp/views.py
@@ -13,6 +13,3 @@
    def GET(self):
        return Response('gotten')
    
    def POST(self):
        return Response('posted')
repoze/bfg/tests/test_integration.py
@@ -151,6 +151,23 @@
        self.assertEqual(browser.get_code(), 200)
        self.assertEqual(browser.get_html(), 'gotten')
class TestViewDecoratorApp(TwillBase):
    config = 'repoze.bfg.tests.viewdecoratorapp:configure.zcml'
    def test_it(self):
        import twill.commands
        browser = twill.commands.get_browser()
        browser.go('http://localhost:6543/first')
        self.assertEqual(browser.get_code(), 200)
        self.failUnless('OK' in browser.get_html())
        browser.go('http://localhost:6543/second')
        self.assertEqual(browser.get_code(), 200)
        self.failUnless('OK2' in browser.get_html())
        browser.go('http://localhost:6543/third')
        self.assertEqual(browser.get_code(), 200)
        self.failUnless('OK3' in browser.get_html())
class DummyContext(object):
    pass
repoze/bfg/tests/test_view.py
@@ -367,6 +367,36 @@
        settings = decorated.__bfg_view_settings__
        self.assertEqual(settings[0]['custom_predicates'], (1,))
    def test_call_with_renderer_nodot(self):
        decorator = self._makeOne(renderer='json')
        def foo():
            """ docstring """
        wrapped = decorator(foo)
        self.failUnless(wrapped is foo)
        settings = wrapped.__bfg_view_settings__[0]
        self.assertEqual(settings['renderer'], 'json')
    def test_call_with_renderer_relpath(self):
        decorator = self._makeOne(renderer='fixtures/minimal.pt')
        def foo():
            """ docstring """
        wrapped = decorator(foo)
        self.failUnless(wrapped is foo)
        settings = wrapped.__bfg_view_settings__[0]
        self.assertEqual(settings['renderer'],
                         'repoze.bfg.tests.test_view:fixtures/minimal.pt')
    def test_call_with_renderer_pkgpath(self):
        decorator = self._makeOne(
            renderer='repoze.bfg.tests.test_view:fixtures/minimal.pt')
        def foo():
            """ docstring """
        wrapped = decorator(foo)
        self.failUnless(wrapped is foo)
        settings = wrapped.__bfg_view_settings__[0]
        self.assertEqual(settings['renderer'],
                         'repoze.bfg.tests.test_view:fixtures/minimal.pt')
class TestDefaultForbiddenView(BaseTest, unittest.TestCase):
    def _callFUT(self, context, request):
        from repoze.bfg.view import default_forbidden_view
repoze/bfg/tests/viewdecoratorapp/__init__.py
New file
@@ -0,0 +1 @@
# package
repoze/bfg/tests/viewdecoratorapp/configure.zcml
New file
@@ -0,0 +1,6 @@
<configure xmlns="http://namespaces.repoze.org/bfg">
  <include package="repoze.bfg.includes" />
  <scan package="."/>
</configure>
repoze/bfg/tests/viewdecoratorapp/views/__init__.py
New file
@@ -0,0 +1 @@
# package
repoze/bfg/tests/viewdecoratorapp/views/templates/foo.pt
New file
@@ -0,0 +1,3 @@
<html>
${result}
</html>
repoze/bfg/tests/viewdecoratorapp/views/views.py
New file
@@ -0,0 +1,17 @@
import os
from repoze.bfg.view import bfg_view
@bfg_view(renderer='templates/foo.pt', name='first')
def first(request):
    return {'result':'OK1'}
@bfg_view(renderer='repoze.bfg.tests.viewdecoratorapp.views:templates/foo.pt',
          name='second')
def second(request):
    return {'result':'OK2'}
here = os.path.normpath(os.path.dirname(os.path.abspath(__file__)))
foo = os.path.join(here, 'templates', 'foo.pt')
@bfg_view(renderer=foo, name='third')
def third(request):
    return {'result':'OK3'}
repoze/bfg/view.py
@@ -1,5 +1,6 @@
import cgi
import mimetypes
import os
import sys
# See http://bugs.python.org/issue5853 which is a recursion bug
@@ -26,7 +27,9 @@
from repoze.bfg.interfaces import IView
from repoze.bfg.path import caller_package
from repoze.bfg.path import package_path
from repoze.bfg.resource import resolve_resource_spec
from repoze.bfg.resource import resource_spec_from_abspath
from repoze.bfg.static import PackageURLParser
from repoze.bfg.threadlocal import get_current_registry
@@ -489,6 +492,20 @@
        else:
            settings = getattr(wrapped, '__bfg_view_settings__', [])
            wrapped.__bfg_view_settings__ = settings
        # try to convert the renderer provided into a fully qualified
        # resource specification
        abspath = setting.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(module)
                    abspath = os.path.join(pp, abspath)
                resource = resource_spec_from_abspath(abspath, module)
                setting['renderer'] = resource
        settings.append(setting)
        return wrapped
repoze/bfg/zcml.py
@@ -30,8 +30,8 @@
from repoze.bfg.authorization import ACLAuthorizationPolicy
from repoze.bfg.configuration import Configurator
from repoze.bfg.exceptions import ConfigurationError
from repoze.bfg.path import package_path
from repoze.bfg.request import route_request_iface
from repoze.bfg.resource import resource_spec_from_abspath
from repoze.bfg.static import StaticRootFactory
from repoze.bfg.threadlocal import get_current_registry
@@ -775,24 +775,14 @@
        )
def path_spec(context, path):
    # Convert an absolute path to a resource in a package to a
    # resource specification if possible; otherwise return the
    # absolute path; we prefer registering resource specifications
    # over absolute paths because these can be overridden by the
    # resource directive.
    # we prefer registering resource specifications over absolute
    # paths because these can be overridden by the resource directive.
    if ':' in path and not os.path.isabs(path):
        # it's already a resource specification
        return path
    abspath = context.path(path)
    if hasattr(context, 'package') and context.package:
        package = context.package
        if getattr(package, '__name__', None) == '__main__':
            return abspath
        pp = package_path(package) + os.path.sep
        if abspath.startswith(pp):
            relpath = abspath[len(pp):]
            return '%s:%s' % (package.__name__,
                              relpath.replace(os.path.sep, '/'))
        return resource_spec_from_abspath(abspath, context.package)
    return abspath
def zcml_configure(name, package):