summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2008-08-17 17:32:54 +0000
committerChris McDonough <chrism@agendaless.com>2008-08-17 17:32:54 +0000
commit0e21c22166f5160a2a64fad714d69d81897ef7d3 (patch)
tree6a576d0edfafe0e204a1770c094f13ff8aa74487
parent157721dda97f5aea95f40e307d9d5dceb1014f83 (diff)
downloadpyramid-0e21c22166f5160a2a64fad714d69d81897ef7d3.tar.gz
pyramid-0e21c22166f5160a2a64fad714d69d81897ef7d3.tar.bz2
pyramid-0e21c22166f5160a2a64fad714d69d81897ef7d3.zip
- Add ``<bfg:settings>`` directive. This directive currently allows
only one attribute: ``reload_templates``. If e.g.:: <bfg:settings reload_templates="true"/> is in your application's ZCML, you will not need to restart the appserver in order for ``z3c.pt`` or XSLT template changes to be detected and displayed.
-rw-r--r--CHANGES.txt11
-rw-r--r--docs/narr/myproject/myproject/configure.zcml6
-rw-r--r--docs/narr/project.rst20
-rw-r--r--repoze/bfg/interfaces.py7
-rw-r--r--repoze/bfg/meta.zcml6
-rw-r--r--repoze/bfg/sampleapp/configure.zcml3
-rw-r--r--repoze/bfg/template.py33
-rw-r--r--repoze/bfg/tests/test_zcml.py31
-rw-r--r--repoze/bfg/zcml.py27
9 files changed, 127 insertions, 17 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 06a3e030e..4717cfce2 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,14 @@
+Next release
+
+ - Add ``<bfg:settings>`` directive. This directive currently allows
+ only one attribute: ``reload_templates``. If e.g.::
+
+ <bfg:settings reload_templates="true"/>
+
+ is in your application's ZCML, you will not need to restart the
+ appserver in order for ``z3c.pt`` or XSLT template changes to be
+ detected and displayed.
+
0.3.0
- Add ``get_template`` API to template module.
diff --git a/docs/narr/myproject/myproject/configure.zcml b/docs/narr/myproject/myproject/configure.zcml
index 174b27354..6cd692784 100644
--- a/docs/narr/myproject/myproject/configure.zcml
+++ b/docs/narr/myproject/myproject/configure.zcml
@@ -2,9 +2,13 @@
xmlns:bfg="http://namespaces.repoze.org/bfg"
i18n_domain="repoze.bfg">
- <!-- this must be included for the view declarations to work -->
+ <!-- this directive must be included for the view declarations to work -->
<include package="repoze.bfg" />
+ <!-- this directive indicates that changes to templates should show up
+ immediately -->
+ <bfg:settings reload_templates="true"/>
+
<bfg:view
for=".models.IMyModel"
view=".views.my_view"
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index f93dd2a88..98f02a476 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -416,7 +416,10 @@ registry`. It looks like so:
#. Line 6 initializes :mod:`repoze.bfg`-specific configuration
directives by including it as a package.
-#. Lines 8-11 register a single view. It is ``for`` model objects
+#. Line 10 tells :mod:`repoze.bfg` to detect changes made to
+ ``z3c.pt`` and XSLT templates immediately.
+
+#. Lines 12-15 register a single view. It is ``for`` model objects
that support the IMyModel interface. The ``view`` attribute points
at a Python function that does all the work for this view. Note
that the values of both the ``for`` attribute and the ``view``
@@ -451,7 +454,16 @@ in the model, and the HTML given back to the browser.
``Request`` class representing the browser's request to our server.
#. The view renders a :term:`template` and returns the result as the
- :term:`response`.
+ :term:`response`. Note that because our ``configure.zcml`` has a
+ ``bfg:settings`` directive indicating that templates should be
+ reloaded when they change, you won't need to restart the
+ application server to see changes you make to templates. During
+ development, this is handy. If this directive had been ``false``
+ (or if the directive did not exist), you would need to restart the
+ application server for each template change. For production
+ applications, you should set your ``bfg:settings``
+ ``reload_templates`` to ``false`` to increase the speed at which
+ templates may be rendered.
.. note::
@@ -460,7 +472,9 @@ in the model, and the HTML given back to the browser.
the ``render_template`` function, also present in
:ref:`template_module`. You may then create your own :term:`WebOb`
Response object, using the result of ``render_template`` as the
- response's body.
+ response's body. There is also a ``get_template`` API in the same
+ module, which you can use to retrieve the template object without
+ rendering it at all, for additional control.
``models.py``
~~~~~~~~~~~~~
diff --git a/repoze/bfg/interfaces.py b/repoze/bfg/interfaces.py
index 4f1a203df..3a4c45373 100644
--- a/repoze/bfg/interfaces.py
+++ b/repoze/bfg/interfaces.py
@@ -27,7 +27,7 @@ class ITraverserFactory(Interface):
""" Return an object that implements IPublishTraverser """
class ITemplateFactory(Interface):
- def __call__(path):
+ def __call__(path, auto_reload=False):
""" Return an an ITemplate given a filesystem path """
class ITemplate(Interface):
@@ -79,3 +79,8 @@ class INewResponse(Interface):
""" An event type that is emitted whenever any repoze.bfg view
returns a response."""
response = Attribute('The response object')
+
+class ISettings(Interface):
+ """ Runtime settings for repoze.bfg """
+ reload_templates = Attribute('Reload templates when they change')
+
diff --git a/repoze/bfg/meta.zcml b/repoze/bfg/meta.zcml
index 72e963ae6..91b4f40e8 100644
--- a/repoze/bfg/meta.zcml
+++ b/repoze/bfg/meta.zcml
@@ -10,6 +10,12 @@
handler=".zcml.view"
/>
+ <meta:directive
+ name="settings"
+ schema=".zcml.ISettingsDirective"
+ handler=".zcml.settings"
+ />
+
</meta:directives>
</configure>
diff --git a/repoze/bfg/sampleapp/configure.zcml b/repoze/bfg/sampleapp/configure.zcml
index a5f27595e..d20d7d8f9 100644
--- a/repoze/bfg/sampleapp/configure.zcml
+++ b/repoze/bfg/sampleapp/configure.zcml
@@ -4,6 +4,9 @@
<include package="repoze.bfg" />
+ <bfg:settings
+ reload_templates="true"/>
+
<utility
provides="repoze.bfg.interfaces.ISecurityPolicy"
factory="repoze.bfg.security.RemoteUserACLSecurityPolicy"
diff --git a/repoze/bfg/template.py b/repoze/bfg/template.py
index 3c60dd19e..de049f5fa 100644
--- a/repoze/bfg/template.py
+++ b/repoze/bfg/template.py
@@ -13,14 +13,19 @@ from zope.interface import implements
from repoze.bfg.interfaces import ITemplateFactory
from repoze.bfg.interfaces import ITemplate
from repoze.bfg.interfaces import INodeTemplate
+from repoze.bfg.interfaces import ISettings
class Z3CPTTemplateFactory(object):
classProvides(ITemplateFactory)
implements(ITemplate)
- def __init__(self, path):
+ def __init__(self, path, auto_reload=False):
from z3c.pt import PageTemplateFile
- self.template = PageTemplateFile(path)
+ try:
+ self.template = PageTemplateFile(path, auto_reload=auto_reload)
+ except TypeError:
+ # z3c.pt before 1.0
+ self.template = PageTemplateFile(path)
def __call__(self, **kw):
result = self.template.render(**kw)
@@ -30,11 +35,12 @@ class XSLTemplateFactory(object):
classProvides(ITemplateFactory)
implements(INodeTemplate)
- def __init__(self, path):
+ def __init__(self, path, auto_reload=False):
self.path = path
+ self.auto_reload = auto_reload
def __call__(self, node, **kw):
- processor = get_processor(self.path)
+ processor = get_processor(self.path, self.auto_reload)
result = str(processor(node, **kw))
return result
@@ -42,13 +48,14 @@ class XSLTemplateFactory(object):
import threading
from lxml import etree
xslt_pool = threading.local()
-def get_processor(xslt_fn):
- try:
- return xslt_pool.processors[xslt_fn]
- except AttributeError:
- xslt_pool.processors = {}
- except KeyError:
- pass
+def get_processor(xslt_fn, auto_reload=False):
+ if not auto_reload:
+ try:
+ return xslt_pool.processors[xslt_fn]
+ except AttributeError:
+ xslt_pool.processors = {}
+ except KeyError:
+ pass
# Make a processor and add it to the pool
source = etree.ElementTree(file=xslt_fn)
@@ -74,7 +81,9 @@ def _get_template(path, **kw):
if template is None:
if not os.path.exists(path):
raise ValueError('Missing template file: %s' % path)
- template = Z3CPTTemplateFactory(path)
+ settings = queryUtility(ISettings)
+ auto_reload = settings and settings.reload_templates
+ template = Z3CPTTemplateFactory(path, auto_reload)
registerTemplate(ITemplate, template, path)
return template
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index 663022796..407eaf24e 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -114,6 +114,37 @@ class TestViewDirective(unittest.TestCase, PlacelessSetup):
self.assertEqual(regadapt['args'][4], '')
self.assertEqual(regadapt['args'][5], None)
+class TestSettingsDirective(unittest.TestCase, PlacelessSetup):
+ def setUp(self):
+ PlacelessSetup.setUp(self)
+
+ def tearDown(self):
+ PlacelessSetup.tearDown(self)
+
+ def _getFUT(self):
+ from repoze.bfg.zcml import settings
+ return settings
+
+ def test_defaults(self):
+ context = DummyContext()
+ settings = self._getFUT()
+ settings(context)
+ actions = context.actions
+ from repoze.bfg.interfaces import ISettings
+ from zope.component.zcml import handler
+ self.assertEqual(len(actions), 1)
+ action = actions[0]
+ self.assertEqual(action['discriminator'], ('settings', ISettings))
+ self.assertEqual(action['callable'], handler)
+ self.assertEqual(len(action['args']), 5)
+ self.assertEqual(action['args'][0], 'registerUtility')
+ settings = action['args'][1]
+ self.assertEqual(settings.reload_templates, False)
+ self.failUnless(ISettings.providedBy(settings), settings)
+ self.assertEqual(action['args'][2], ISettings)
+ self.assertEqual(action['args'][3], '')
+ self.assertEqual(action['args'][4], context.info)
+
class TestSampleApp(unittest.TestCase, PlacelessSetup):
def setUp(self):
PlacelessSetup.setUp(self)
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index 343abc1d7..bb37c6581 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -2,17 +2,44 @@ from zope.component.zcml import handler
from zope.component.interface import provideInterface
from zope.configuration.exceptions import ConfigurationError
from zope.configuration.fields import GlobalObject
+from zope.configuration.fields import Bool
from zope.interface import Interface
+from zope.interface import implements
from zope.schema import TextLine
from repoze.bfg.interfaces import IRequest
from repoze.bfg.interfaces import IViewPermission
from repoze.bfg.interfaces import IView
+from repoze.bfg.interfaces import ISettings
from repoze.bfg.security import ViewPermissionFactory
+def _handler(*arg, **kw):
+ import pdb; pdb.set_trace()
+ return handler(*arg, **kw)
+
+class Settings(object):
+ implements(ISettings)
+ def __init__(self, reload_templates=False):
+ self.reload_templates = reload_templates
+
+def settings(_context, reload_templates=False):
+ settings = Settings(reload_templates=reload_templates)
+ _context.action(
+ discriminator = ('settings', ISettings),
+ callable = handler,
+ args = ('registerUtility', settings, ISettings, '', _context.info),
+ )
+
+class ISettingsDirective(Interface):
+ reload_templates = Bool(
+ title=u"Reload templates when they change",
+ description=(u"Specifies whether templates should be reloaded when"
+ "a change is made"),
+ default=False)
+
def view(_context,
permission=None,
for_=None,