From 5babbaf48a71af786d41f59a1f1f7d5272a60a4d Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Thu, 17 Dec 2009 01:17:48 +0000 Subject: - When Chameleon page or text templates were added imperatively (via ``Configurator.add_view`` or some derivative), they too-eagerly attempted to look up the ``reload_templates`` setting via ``get_settings``, meaning they were always registered in non-auto-reload-mode (the default). Each now waits until its respective ``template`` attribute is accessed to look up the value. --- repoze/bfg/chameleon_text.py | 7 ++++++- repoze/bfg/chameleon_zpt.py | 9 ++++++++- repoze/bfg/decorator.py | 15 +++++++++++++++ repoze/bfg/tests/test_chameleon_text.py | 7 +++++++ repoze/bfg/tests/test_chameleon_zpt.py | 7 +++++++ repoze/bfg/tests/test_decorator.py | 23 +++++++++++++++++++++++ 6 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 repoze/bfg/decorator.py create mode 100644 repoze/bfg/tests/test_decorator.py (limited to 'repoze') diff --git a/repoze/bfg/chameleon_text.py b/repoze/bfg/chameleon_text.py index 50491a3d0..9cb1c989a 100644 --- a/repoze/bfg/chameleon_text.py +++ b/repoze/bfg/chameleon_text.py @@ -20,6 +20,7 @@ except ImportError: # pragma: no cover 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.settings import get_settings from repoze.bfg.threadlocal import get_current_registry @@ -40,9 +41,13 @@ def renderer_factory(path): class TextTemplateRenderer(object): implements(ITemplateRenderer) def __init__(self, path): + self.path = path + + @reify + def template(self): settings = get_settings() auto_reload = settings and settings['reload_templates'] - self.template = TextTemplateFile(path, auto_reload=auto_reload) + return TextTemplateFile(self.path, auto_reload=auto_reload) def implementation(self): return self.template diff --git a/repoze/bfg/chameleon_zpt.py b/repoze/bfg/chameleon_zpt.py index bc1a5d22f..9083ff3f7 100644 --- a/repoze/bfg/chameleon_zpt.py +++ b/repoze/bfg/chameleon_zpt.py @@ -13,6 +13,7 @@ except ImportError, why: # pragma: no cover 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.settings import get_settings from repoze.bfg.threadlocal import get_current_registry @@ -23,9 +24,15 @@ def renderer_factory(path): class ZPTTemplateRenderer(object): implements(ITemplateRenderer) def __init__(self, path): + self.path = path + + @reify + def template(self): + # delay template creation until (hopefully) settings have been + # registered settings = get_settings() auto_reload = settings and settings['reload_templates'] - self.template = PageTemplateFile(path, auto_reload=auto_reload) + return PageTemplateFile(self.path, auto_reload=auto_reload) def implementation(self): return self.template diff --git a/repoze/bfg/decorator.py b/repoze/bfg/decorator.py new file mode 100644 index 000000000..69c1e65e2 --- /dev/null +++ b/repoze/bfg/decorator.py @@ -0,0 +1,15 @@ +class reify(object): + + """ Put the result of a method which uses this (non-data) + descriptor decorator in the instance dict after the first call, + effectively replacing the decorator with an instance variable.""" + + def __init__(self, wrapped): + self.wrapped = wrapped + + def __get__(self, inst, objtype=None): + if inst is None: + return self + val = self.wrapped(inst) + setattr(inst, self.wrapped.__name__, val) + return val diff --git a/repoze/bfg/tests/test_chameleon_text.py b/repoze/bfg/tests/test_chameleon_text.py index 54ffc8b10..007c102e3 100644 --- a/repoze/bfg/tests/test_chameleon_text.py +++ b/repoze/bfg/tests/test_chameleon_text.py @@ -47,6 +47,13 @@ class TextTemplateRendererTests(Base, unittest.TestCase): from repoze.bfg.interfaces import ITemplateRenderer verifyClass(ITemplateRenderer, self._getTargetClass()) + def test_template_reified(self): + minimal = self._getTemplatePath('minimal.txt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template, instance.__dict__['template']) + def test_call(self): minimal = self._getTemplatePath('minimal.txt') instance = self._makeOne(minimal) diff --git a/repoze/bfg/tests/test_chameleon_zpt.py b/repoze/bfg/tests/test_chameleon_zpt.py index 38ef543cf..e4bf8f766 100644 --- a/repoze/bfg/tests/test_chameleon_zpt.py +++ b/repoze/bfg/tests/test_chameleon_zpt.py @@ -48,6 +48,13 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): self.assertEqual(result, '
\n
') + def test_template_reified(self): + minimal = self._getTemplatePath('minimal.pt') + instance = self._makeOne(minimal) + self.failIf('template' in instance.__dict__) + template = instance.template + self.assertEqual(template, instance.__dict__['template']) + def test_call_with_nondict_value(self): minimal = self._getTemplatePath('minimal.pt') instance = self._makeOne(minimal) diff --git a/repoze/bfg/tests/test_decorator.py b/repoze/bfg/tests/test_decorator.py new file mode 100644 index 000000000..b1ac2252d --- /dev/null +++ b/repoze/bfg/tests/test_decorator.py @@ -0,0 +1,23 @@ +import unittest + +class TestReify(unittest.TestCase): + def _makeOne(self, wrapped): + from repoze.bfg.decorator import reify + return reify(wrapped) + + def test___get__withinst(self): + def wrapped(inst): + return 'a' + decorator = self._makeOne(wrapped) + inst = Dummy() + result = decorator.__get__(inst) + self.assertEqual(result, 'a') + self.assertEqual(inst.__dict__['wrapped'], 'a') + + def test___get__noinst(self): + decorator = self._makeOne(None) + result = decorator.__get__(None) + self.assertEqual(result, decorator) + +class Dummy(object): + pass -- cgit v1.2.3