[Checkins] SVN: z3c.jbot/trunk/ Added support for per-layer
overrides.
Malthe Borch
mborch at gmail.com
Mon Jul 14 13:33:54 EDT 2008
Log message for revision 88363:
Added support for per-layer overrides.
Changed:
U z3c.jbot/trunk/README.txt
U z3c.jbot/trunk/setup.py
U z3c.jbot/trunk/z3c/jbot/Five.txt
U z3c.jbot/trunk/z3c/jbot/README.txt
U z3c.jbot/trunk/z3c/jbot/__init__.py
A z3c.jbot/trunk/z3c/jbot/interfaces.py
U z3c.jbot/trunk/z3c/jbot/manager.py
U z3c.jbot/trunk/z3c/jbot/metaconfigure.py
U z3c.jbot/trunk/z3c/jbot/metadirectives.py
U z3c.jbot/trunk/z3c/jbot/tests/test_doctests.py
A z3c.jbot/trunk/z3c/jbot/utility.py
-=-
Modified: z3c.jbot/trunk/README.txt
===================================================================
--- z3c.jbot/trunk/README.txt 2008-07-14 17:23:41 UTC (rev 88362)
+++ z3c.jbot/trunk/README.txt 2008-07-14 17:33:53 UTC (rev 88363)
@@ -9,6 +9,9 @@
template overrides directory and give your new template the canonical
filename.
+Overrides may be registered for a specific request-layer.
+
+
Canonical filename
------------------
@@ -21,16 +24,25 @@
Suppose you want to override: /plone/app/layout/viewlets/logo.pt
You would use the filename: plone.app.layout.viewlets.logo.pt
+
Registering a on overrides directory
------------------------------------
In python:
- >>> from z3c.jbot.manager import getGlobalTemplateManager
- >>> getGlobalTemplateManager().registerDirectory(directory)
+ >>> from z3c.jbot.utility import getManager
+ >>> getManager().registerDirectory(directory)
-In ZCML:
+Using ZCML:
<include package="z3c.jbot" file="meta.zcml" />
- <browser:templateOverrides directory="<directory>" />
+ <browser:templateOverrides
+ directory="<directory>"
+ layer="<layer>" />
+
+
+Author
+------
+
+Malthe Borch <mborch at gmail.com>
Modified: z3c.jbot/trunk/setup.py
===================================================================
--- z3c.jbot/trunk/setup.py 2008-07-14 17:23:41 UTC (rev 88362)
+++ z3c.jbot/trunk/setup.py 2008-07-14 17:33:53 UTC (rev 88363)
@@ -1,7 +1,7 @@
from setuptools import setup, find_packages
import sys, os
-version = '0.1.6dev'
+version = '0.2dev'
setup(name='z3c.jbot',
version=version,
@@ -27,6 +27,9 @@
'zope.pagetemplate',
'zope.component',
'zope.configuration',
+ 'zope.security',
+ 'zope.publisher',
+ 'zope.app.component',
# -*- Extra requirements: -*-
],
entry_points="""
Modified: z3c.jbot/trunk/z3c/jbot/Five.txt
===================================================================
--- z3c.jbot/trunk/z3c/jbot/Five.txt 2008-07-14 17:23:41 UTC (rev 88362)
+++ z3c.jbot/trunk/z3c/jbot/Five.txt 2008-07-14 17:33:53 UTC (rev 88363)
@@ -28,10 +28,52 @@
Register template overrides directory.
>>> import z3c.jbot.manager
- >>> manager = z3c.jbot.manager.getGlobalTemplateManager()
+ >>> import z3c.jbot.interfaces
+
+ >>> factory = z3c.jbot.manager.TemplateManagerFactory()
+ >>> component.provideAdapter(
+ ... factory, (interface.Interface,), z3c.jbot.interfaces.ITemplateManager)
+
+ >>> manager = factory.manager
>>> manager.registerDirectory(directory)
Verify that the override is rendered.
>>> view.template()
u'This template will override the example template.\n'
+
+ >>> manager.unregisterDirectory(directory)
+
+Register override for HTTP-request layer.
+
+ >>> from zope.publisher.interfaces.browser import IHTTPRequest
+ >>> factory = z3c.jbot.manager.TemplateManagerFactory()
+ >>> component.provideAdapter(
+ ... factory, (IHTTPRequest,), z3c.jbot.interfaces.ITemplateManager)
+
+ >>> manager = factory.manager
+ >>> manager.registerDirectory(directory)
+
+ >>> view.template()
+ u'This is an example page template.\n'
+
+On Zope 2, the request is acquired from the site as set by the
+component site hook.
+
+ >>> from zope.publisher.browser import TestRequest
+ >>> class MockSite(object):
+ ... REQUEST = TestRequest()
+ ... getSiteManager = component.getSiteManager
+
+ >>> from zope.app.component.hooks import setHooks, setSite
+ >>> setHooks()
+ >>> setSite(MockSite())
+
+Examine the request.
+
+ >>> import z3c.jbot.utility
+ >>> z3c.jbot.utility.getRequest()
+ <zope.publisher.browser.TestRequest ...>
+
+ >>> z3c.jbot.utility.getManager() is manager
+ True
Modified: z3c.jbot/trunk/z3c/jbot/README.txt
===================================================================
--- z3c.jbot/trunk/z3c/jbot/README.txt 2008-07-14 17:23:41 UTC (rev 88362)
+++ z3c.jbot/trunk/z3c/jbot/README.txt 2008-07-14 17:33:53 UTC (rev 88363)
@@ -30,8 +30,18 @@
>>> import z3c.jbot.tests
>>> directory = z3c.jbot.tests.__path__[0]
+Register template manager factory. We'll register it for
+``zope.interface.Interface`` which makes it available on all layers.
+
>>> import z3c.jbot.manager
- >>> manager = z3c.jbot.manager.getGlobalTemplateManager()
+ >>> import z3c.jbot.interfaces
+ >>> factory = z3c.jbot.manager.TemplateManagerFactory()
+ >>> component.provideAdapter(
+ ... factory, (interface.Interface,), z3c.jbot.interfaces.ITemplateManager)
+
+Register overrides directory.
+
+ >>> manager = factory.manager
>>> manager.registerDirectory("%s/templates" % directory)
Verify that we've registered the contents of the directory:
@@ -63,6 +73,71 @@
>>> template.filename
'.../z3c.jbot/z3c/jbot/tests/templates/example.pt'
+Overrides can be registered for a specific layer. Let's re-register an
+override template factory for the HTTP-request layer.
+
+ >>> from zope.publisher.interfaces.browser import IHTTPRequest
+ >>> factory = z3c.jbot.manager.TemplateManagerFactory()
+ >>> component.provideAdapter(
+ ... factory, (IHTTPRequest,), z3c.jbot.interfaces.ITemplateManager)
+
+Register overrides directory.
+
+ >>> manager = factory.manager
+ >>> manager.registerDirectory("%s/templates" % directory)
+
+Let's set up an interaction with a base request.
+
+ >>> import zope.security.management
+ >>> import zope.publisher.base
+ >>> request = zope.publisher.base.BaseRequest("", {})
+ >>> IHTTPRequest.providedBy(request)
+ False
+ >>> zope.security.management.newInteraction(request)
+
+Since this request is not an HTTP-request, we don't expect the
+override to be enabled.
+
+ >>> template()
+ u'This is an example page template.\n'
+
+Let's now engage in an interaction with an HTTP-request.
+
+ >>> interface.alsoProvides(request, IHTTPRequest)
+ >>> template()
+ u'This template will override the example template.\n'
+
+ >>> template._v_cooked
+ 1
+
+Going back to a basic request.
+
+ >>> interface.noLongerProvides(request, IHTTPRequest)
+ >>> IHTTPRequest.providedBy(request)
+ False
+
+ >>> template()
+ u'This is an example page template.\n'
+
+Let's verify that we only cook once per template source.
+
+ >>> import z3c.jbot.utility
+ >>> z3c.jbot.utility.getManager().registerTemplate(template)
+ >>> template._v_last_read and template._v_cooked
+ 1
+
+ >>> interface.alsoProvides(request, IHTTPRequest)
+ >>> z3c.jbot.utility.getManager().registerTemplate(template)
+ >>> template._v_last_read and template._v_cooked
+ 1
+
+ >>> template()
+ u'This template will override the example template.\n'
+
+ >>> z3c.jbot.utility.getManager().unregisterDirectory("%s/templates" % directory)
+ >>> interface.noLongerProvides(request, IHTTPRequest)
+ >>> z3c.jbot.utility.getManager().unregisterDirectory("%s/templates" % directory)
+
Configuring template override directories in ZCML
-------------------------------------------------
@@ -86,4 +161,23 @@
>>> template()
u'This template will override the example template.\n'
- >>> manager.unregisterDirectory("%s/templates" % directory)
+ >>> z3c.jbot.utility.getManager().unregisterDirectory("%s/templates" % directory)
+
+Let's register overrides for the HTTP-request layer.
+
+ >>> xmlconfig.xmlconfig(StringIO("""
+ ... <configure xmlns="http://namespaces.zope.org/browser">
+ ... <templateOverrides
+ ... directory="%s/templates"
+ ... layer="zope.publisher.interfaces.browser.IHTTPRequest" />
+ ... </configure>
+ ... """ % directory))
+
+ >>> template()
+ u'This is an example page template.\n'
+
+If we now provide the HTTP-request layer, the override becomes active.
+
+ >>> interface.alsoProvides(request, IHTTPRequest)
+ >>> template()
+ u'This template will override the example template.\n'
Modified: z3c.jbot/trunk/z3c/jbot/__init__.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/__init__.py 2008-07-14 17:23:41 UTC (rev 88362)
+++ z3c.jbot/trunk/z3c/jbot/__init__.py 2008-07-14 17:33:53 UTC (rev 88363)
@@ -1,13 +1,48 @@
-import manager
from zope.pagetemplate.pagetemplatefile import PageTemplateFile
-# add registration hook to ``zope.app.pagetemplate``
+import manager
+import utility
+
+NO_DEFAULT = object()
+
+class LayerProperty(property):
+ """Layer-specific property class.
+
+ Instance attributes are instance *and* layer-specific when defined
+ using this property class.
+ """
+
+ def __init__(self, name):
+ self.name = name
+ self.default = getattr(PageTemplateFile, name, NO_DEFAULT)
+ property.__init__(self, self._get, self._set)
+
+ def _get(self, template):
+ layer = utility.getLayer()
+ attributes = getattr(template, '_v_attrs', template.__dict__)
+ key = self.name
+ if (layer, key) in attributes:
+ return attributes[layer, key]
+
+ return self.default
+
+ def _set(self, template, value):
+ layer = utility.getLayer()
+ template.__dict__.setdefault('_v_attrs', {})[layer, self.name] = value
+ if self.default is NO_DEFAULT:
+ self.default = value
+
+# registration hook to template manager
def jbot(func):
- def jbot_func(self, *args, **kwargs):
- manager.getGlobalTemplateManager().registerTemplate(self)
+ def patch(self, *args, **kwargs):
+ manager = utility.getManager()
+ if manager is not None:
+ manager.registerTemplate(self)
+
return func(self, *args, **kwargs)
- return jbot_func
+ return patch
+# patch ``_cook_check``-method to insert jbot-logic
PageTemplateFile._cook_check = jbot(PageTemplateFile._cook_check)
try:
from Products.PageTemplates.PageTemplateFile import PageTemplateFile as Z2PageTemplateFile
@@ -15,4 +50,7 @@
except ImportError:
pass
-
+# munge per-layer attribute descriptors on class
+for name in ('_v_macros', '_v_program', '_v_cooked', '_v_errors',
+ '_v_last_read', '_v_warning', '_text_', 'filename'):
+ setattr(PageTemplateFile, name, LayerProperty(name))
Added: z3c.jbot/trunk/z3c/jbot/interfaces.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/interfaces.py (rev 0)
+++ z3c.jbot/trunk/z3c/jbot/interfaces.py 2008-07-14 17:33:53 UTC (rev 88363)
@@ -0,0 +1,10 @@
+from zope import interface
+
+class ITemplateManager(interface.Interface):
+ def registerDirectory(directory):
+ pass
+
+ def unregisterDirectory(directory):
+ pass
+
+
Modified: z3c.jbot/trunk/z3c/jbot/manager.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/manager.py 2008-07-14 17:23:41 UTC (rev 88362)
+++ z3c.jbot/trunk/z3c/jbot/manager.py 2008-07-14 17:33:53 UTC (rev 88363)
@@ -1,6 +1,10 @@
+from zope import interface
+
import sys
import os.path
+import interfaces
+
IGNORE = object()
def root_length(a, b):
@@ -29,62 +33,86 @@
path = path[1:]
return path
+
+class TemplateManagerFactory(object):
+ def __init__(self):
+ self.manager = TemplateManager()
+
+ def __call__(self, layer):
+ return self.manager
-class GlobalTemplateManager(object):
+class TemplateManager(object):
+ interface.implements(interfaces.ITemplateManager)
+
def __init__(self):
self.syspaths = tuple(sys.path)
self.templates = {}
self.paths = {}
-
+
def registerDirectory(self, directory):
for filename in os.listdir(directory):
if filename.endswith('.pt'):
self.paths[filename] = "%s/%s" % (directory, filename)
+ for template, filename in self.templates.items():
+ if filename is IGNORE:
+ del self.templates[template]
+
def unregisterDirectory(self, directory):
+ templates = []
+
+ for template, filename in self.templates.items():
+ if filename in self.paths:
+ templates.append(template)
+
for filename in os.listdir(directory):
if filename in self.paths:
del self.paths[filename]
+
+ for template in templates:
+ template._v_last_read = False
def registerTemplate(self, template):
# only register templates that have a filename attribute
if not hasattr(template, 'filename'):
return
- # assert that template is not already registered
+ # assert that the template is not already registered
filename = self.templates.get(template)
if filename is IGNORE:
return
- if self.paths.get(filename) == template.filename:
+ # if the template filename matches an override, we're done
+ paths = self.paths
+ if paths.get(filename) == template.filename:
return
- if filename is not None and filename not in self.paths:
- # template file has been unregistered; restore
- # original template
+ # verify that override has not been unregistered
+ if filename is not None and filename not in paths:
+ # restore original template
template.filename = template._filename
delattr(template, '_filename')
-
+ del self.templates[template]
+
+ # check if an override exists
path = find_package(self.syspaths, template.filename)
if path is None:
+ # permanently ignore template
self.templates[template] = IGNORE
return
filename = path.replace(os.path.sep, '.')
+ if filename in paths:
+ path = paths[filename]
- if filename in self.paths:
- path = self.paths[filename]
-
# save original filename
template._filename = template.filename
# save template and registry and assign path
template.filename = path
self.templates[template] = filename
+ else:
+ self.templates[template] = IGNORE
+ # force cook
template._v_last_read = False
-
-GlobalTemplateManager = GlobalTemplateManager()
-
-def getGlobalTemplateManager():
- return GlobalTemplateManager
Modified: z3c.jbot/trunk/z3c/jbot/metaconfigure.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/metaconfigure.py 2008-07-14 17:23:41 UTC (rev 88362)
+++ z3c.jbot/trunk/z3c/jbot/metaconfigure.py 2008-07-14 17:33:53 UTC (rev 88363)
@@ -1,5 +1,24 @@
+from zope import interface
from zope import component
-from z3c.jbot.manager import getGlobalTemplateManager
-def templateOverridesDirective(_context, directory):
- getGlobalTemplateManager().registerDirectory(directory)
+import manager
+import interfaces
+
+def handler(directory, layer):
+ gsm = component.getGlobalSiteManager()
+
+ # check if a template manager already exists
+ factory = gsm.adapters.lookup((layer,), interfaces.ITemplateManager)
+
+ if factory is None:
+ factory = TemplateManagerFactory()
+ component.provideAdapter(factory, (layer,), interfaces.ITemplateManager)
+
+ factory.manager.registerDirectory(directory)
+
+def templateOverridesDirective(_context, directory, layer=interface.Interface):
+ _context.action(
+ discriminator = ('override', directory, layer),
+ callable = handler,
+ args = (directory, layer),
+ )
Modified: z3c.jbot/trunk/z3c/jbot/metadirectives.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/metadirectives.py 2008-07-14 17:23:41 UTC (rev 88362)
+++ z3c.jbot/trunk/z3c/jbot/metadirectives.py 2008-07-14 17:33:53 UTC (rev 88363)
@@ -1,5 +1,6 @@
from zope.interface import Interface
from zope.configuration import fields
+from zope.app.component.back35 import LayerField
class ITemplateOverridesDirective(Interface):
"""Directive which registers a directory with template overrides."""
@@ -8,3 +9,9 @@
title=u"Path to directory",
required=True)
+ layer = LayerField(
+ title=u"The layer the overrides should be enabled for",
+ description=u"By default overrides are used for all layers.",
+ required=False
+ )
+
Modified: z3c.jbot/trunk/z3c/jbot/tests/test_doctests.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/tests/test_doctests.py 2008-07-14 17:23:41 UTC (rev 88362)
+++ z3c.jbot/trunk/z3c/jbot/tests/test_doctests.py 2008-07-14 17:33:53 UTC (rev 88363)
@@ -1,15 +1,19 @@
+import zope.interface
+import zope.component
import zope.testing
import unittest
-OPTIONFLAGS = (zope.testing.doctest.REPORT_ONLY_FIRST_FAILURE |
- zope.testing.doctest.ELLIPSIS |
+OPTIONFLAGS = (zope.testing.doctest.ELLIPSIS |
zope.testing.doctest.NORMALIZE_WHITESPACE)
import zope.component.testing
def test_suite():
doctests = ['README.txt']
-
+ globs = dict(
+ interface=zope.interface,
+ component=zope.component)
+
try:
import Products.Five
doctests.append('Five.txt')
@@ -21,6 +25,7 @@
optionflags=OPTIONFLAGS,
setUp=zope.component.testing.setUp,
tearDown=zope.component.testing.tearDown,
+ globs=globs,
package="z3c.jbot") for doctest in doctests
))
Added: z3c.jbot/trunk/z3c/jbot/utility.py
===================================================================
--- z3c.jbot/trunk/z3c/jbot/utility.py (rev 0)
+++ z3c.jbot/trunk/z3c/jbot/utility.py 2008-07-14 17:33:53 UTC (rev 88363)
@@ -0,0 +1,47 @@
+from zope import interface
+from zope import component
+
+from zope.app.component.hooks import getSite
+from zope.publisher.interfaces import IRequest
+
+import zope.security.management
+import zope.security.interfaces
+
+import interfaces
+
+try:
+ import Products.PageTemplates
+ ZOPE_2 = True
+except ImportError:
+ ZOPE_2 = False
+
+def getRequest():
+ try:
+ i = zope.security.management.getInteraction()
+ except zope.security.interfaces.NoInteraction:
+ if ZOPE_2:
+ # get request by acquisition
+ site = getSite()
+ if site is not None:
+ return site.REQUEST
+ return
+
+ for p in i.participations:
+ if IRequest.providedBy(p):
+ return p
+
+def getLayer():
+ request = getRequest()
+
+ if request is not None:
+ return interface.providedBy(request)
+
+ return interface.Interface
+
+def getManager():
+ layer = getLayer()
+ gsm = component.getGlobalSiteManager()
+
+ factory = gsm.adapters.lookup((layer,), interfaces.ITemplateManager)
+ if factory is not None:
+ return factory.manager
More information about the Checkins
mailing list