[Checkins] SVN: grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/te We (Martijn Faassen, Vincent Fretin) are refactoring the template registry
Martijn Faassen
faassen at startifact.com
Sat Jul 4 09:05:00 EDT 2009
Log message for revision 101521:
We (Martijn Faassen, Vincent Fretin) are refactoring the template registry
logic to hopefully simplify it a lot. We are aiming at a single global template
registry that keeps track of which templates are around and whether they're
associated or not.
We check this in as a checkpoint: the new template registry is not in use
by the grokkers yet.
Changed:
U grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/templatereg.py
A grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/templatereg.txt
U grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/tests/test_all.py
-=-
Modified: grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/templatereg.py
===================================================================
--- grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/templatereg.py 2009-07-04 12:43:28 UTC (rev 101520)
+++ grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/templatereg.py 2009-07-04 13:05:00 UTC (rev 101521)
@@ -6,7 +6,98 @@
from martian.error import GrokError
from grokcore.view.interfaces import ITemplateFileFactory
from grokcore.view.components import PageTemplate
+
+class TemplateRegistry(object):
+ def __init__(self):
+ self._reg = {}
+ self._unassociated = set()
+
+ def register_directory_for_module(self, module_info):
+ # we cannot register a templates dir for a package
+ if module_info.isPackage():
+ return
+
+ template_dir = self._get_template_dir(module_info)
+ self.register_directory(template_dir)
+
+ def register_directory(self, template_dir):
+ # we can only register for directories
+ if not os.path.isdir(template_dir):
+ return
+
+ for template_file in os.listdir(template_dir):
+ self._register_template_file(
+ os.path.join(template_dir, template_file))
+
+ def _register_template_file(self, template_path):
+ template_dir, template_file = os.path.split(template_path)
+
+ if template_file.startswith('.') or template_file.endswith('~'):
+ return
+ if template_file.endswith('.cache'):
+ # chameleon creates '<tpl_name>.cache' files on the fly
+ return
+
+# inline_template = self.getLocal(template_name)
+# if inline_template:
+# raise GrokError("Conflicting templates found for name '%s' "
+# "in module %r, either inline and in template "
+# "directory '%s', or two templates with the "
+# "same name and different extensions."
+# % (template_name, module_info.getModule(),
+# template_dir), inline_template)
+
+ template_name, extension = os.path.splitext(template_file)
+ if (template_dir, template_name) in self._reg:
+ raise GrokError("Conflicting templates found for name '%s' "
+ "in directory '%s': multiple templates with "
+ "the same name and different extensions ." %
+ (template_name, template_dir), None)
+
+ extension = extension[1:] # Get rid of the leading dot.
+ template_factory = zope.component.queryUtility(
+ grokcore.view.interfaces.ITemplateFileFactory,
+ name=extension)
+
+ if template_factory is None:
+ # Warning when importing files. This should be
+ # allowed because people may be using editors that generate
+ # '.bak' files and such.
+ warnings.warn("File '%s' has an unrecognized extension in "
+ "directory '%s'" %
+ (template_file, template_dir), UserWarning, 2)
+ return
+ template = template_factory(template_file, template_dir)
+ # XXX this isn't defined in the interface or anywhere...
+ template._annotateGrokInfo(template_name, template_path)
+
+ self._reg[(template_dir, template_name)] = template
+ self._unassociated.add(template_path)
+
+
+ def associate(self, template_file):
+ self._unassociated.remove(template_file)
+
+ def lookup(self, template_dir, template_name):
+ result = self._reg.get((template_dir, template_name))
+ if result is None:
+ raise LookupError("template '%s' in '%s' cannot be found" % (
+ template_name, template_dir))
+ return result
+
+ def unassociated(self):
+ return self._unassociated
+
+ def _get_template_dir(self, module_info):
+ template_dir_name = grokcore.view.templatedir.bind().get(
+ module=module_info.getModule())
+ if template_dir_name is None:
+ template_dir_name = module_info.name + '_templates'
+
+ template_dir = module_info.getResourcePath(template_dir_name)
+ return template_dir
+
all_directory_templates_registries = {}
Added: grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/templatereg.txt
===================================================================
--- grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/templatereg.txt (rev 0)
+++ grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/templatereg.txt 2009-07-04 13:05:00 UTC (rev 101521)
@@ -0,0 +1,178 @@
+Template registry
+=================
+
+When grokking views, there is a single global template registry that:
+
+* the grokking process can issue actions to register templates into it.
+
+* the actions that set up views can quickly look up templates from it
+ to associate them with views.
+
+* the template registry keeps track of templates that are associated
+ with views, or not.
+
+* an action gets registered that gets executed late in configuration
+ process that reports on any unassociated templates that are left.
+
+Setup
+-----
+
+We create our global template registry once::
+
+ >>> from grokcore.view.templatereg import TemplateRegistry
+ >>> reg = TemplateRegistry()
+
+For testing purposes we will create a directory with two templates in it::
+
+ >>> import os, tempfile
+ >>> templates_dir = tempfile.mkdtemp()
+ >>> f = open(os.path.join(templates_dir, 'foo.template'), 'w')
+ >>> f.write('foo')
+ >>> f.close()
+ >>> f = open(os.path.join(templates_dir, 'bar.template'), 'w')
+ >>> f.write('bar')
+ >>> f.close()
+
+Our templates are ``.template``, so we need to register a
+``ITemplateFileFactory`` utility for them that knows how to make the
+appropriate templates::
+
+ >>> from grokcore.view.interfaces import ITemplateFileFactory, ITemplate
+ >>> from zope.interface import implements
+ >>> class TestTemplate(object):
+ ... implements(ITemplate) # we lie for testing purposes
+ ... def __init__(self, filename, source):
+ ... self.filename = filename
+ ... self.source = source
+ ... def __repr__(self):
+ ... path, filename = os.path.split(self.filename)
+ ... return "<Template '%s' in '%s'>" % (filename, path)
+ ... def __cmp__(self, other):
+ ... return cmp(self.filename, other.filename)
+ ... def _annotateGrokInfo(self, template_name, template_path):
+ ... pass # XXX why do we need to implement this?
+
+ >>> class TestTemplateFactory(object):
+ ... implements(ITemplateFileFactory)
+ ... def __call__(self, filename, _prefix=None):
+ ... f = open(os.path.join(_prefix, filename), 'r')
+ ... data = f.read()
+ ... f.close()
+ ... return TestTemplate(filename, data)
+
+ >>> from zope import component
+ >>> component.provideUtility(TestTemplateFactory(), ITemplateFileFactory,
+ ... name='template')
+
+Registering a directory
+-----------------------
+
+We can now register the filesystem templates with the registry::
+
+ >>> reg.register_directory(templates_dir)
+
+We can look up the templates in the registry now::
+
+ >>> reg.lookup(templates_dir, 'foo')
+ <Template 'foo.template' in '...'>
+ >>> reg.lookup(templates_dir, 'bar')
+ <Template 'bar.template' in '...'>
+
+If we try to look up a template in a directory that doesn't exist, we get
+a LookupError::
+
+ >>> reg.lookup('certainlydoesntexist', 'foo')
+ Traceback (most recent call last):
+ ...
+ LookupError: template 'foo' in 'certainlydoesntexist' cannot be found
+
+We get this error for templates that do not exist as well::
+
+ >>> reg.lookup(templates_dir, 'doesntexist')
+ Traceback (most recent call last):
+ ...
+ LookupError: template 'doesntexist' in ... cannot be found
+
+Since no templates have yet been associated, retrieving the unassociated
+templates will get us all registered templates::
+
+ >>> sorted(reg.unassociated())
+ ['...bar.template', '...foo.template']
+
+Now we use a template, so we mark it as associated::
+
+ >>> reg.associate(os.path.join(templates_dir, 'foo.template'))
+
+There is only a single unassociated template left now::
+
+ >>> sorted(reg.unassociated())
+ ['...bar.template']
+
+Unknown template extensions
+---------------------------
+
+We set up a directory with a template language that is not recognized by
+the system::
+
+ >>> import os, tempfile
+ >>> templates_dir2 = tempfile.mkdtemp()
+ >>> f = open(os.path.join(templates_dir2, 'foo.unknown'), 'w')
+ >>> f.write('unknown')
+ >>> f.close()
+
+We will now start recording all the warnings, as we will get one about the
+unknown template language when we register the directory later::
+
+ >>> from grokcore.view.testing import warn
+ >>> import warnings
+ >>> saved_warn = warnings.warn
+ >>> warnings.warn = warn
+
+We register the directory now, and we get the warning::
+
+ >>> reg.register_directory(templates_dir2)
+ From grok.testing's warn():
+ ... UserWarning: File 'foo.unknown' has an unrecognized extension in directory '...'
+ ...
+
+We restore the normal warnings mechanism::
+
+ >>> warnings.warn = saved_warn
+
+This file will not be loaded as a template::
+
+ >>> reg.lookup(templates_dir2, 'foo.unknown')
+ Traceback (most recent call last):
+ ...
+ LookupError: template 'foo.unknown' in '...' cannot be found
+
+Multiple templates with the same name
+-------------------------------------
+
+Let's make the template languages ``1`` and ``2`` known::
+
+ >>> component.provideUtility(TestTemplateFactory(), ITemplateFileFactory,
+ ... name='1')
+ >>> component.provideUtility(TestTemplateFactory(), ITemplateFileFactory,
+ ... name='2')
+
+We now set up a directory which contains 'foo.1' and 'foo.2'. These
+templates have the same name but use different template languages, and
+Grok won't know which one it should use::
+
+ >>> import os, tempfile
+ >>> templates_dir3 = tempfile.mkdtemp()
+ >>> f = open(os.path.join(templates_dir3, 'foo.1'), 'w')
+ >>> f.write('1')
+ >>> f.close()
+ >>> f = open(os.path.join(templates_dir3, 'foo.2'), 'w')
+ >>> f.write('2')
+ >>> f.close()
+
+We expect an error when we register this directory::
+
+ >>> reg.register_directory(templates_dir3)
+ Traceback (most recent call last):
+ ...
+ GrokError: Conflicting templates found for name 'foo' in directory '...':
+ multiple templates with the same name and different extensions .
Modified: grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/tests/test_all.py
===================================================================
--- grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/tests/test_all.py 2009-07-04 12:43:28 UTC (rev 101520)
+++ grokcore.view/branches/shared_templates_userwarning/src/grokcore/view/tests/test_all.py 2009-07-04 13:05:00 UTC (rev 101521)
@@ -4,6 +4,8 @@
from zope.testing import doctest, cleanup, renormalizing
import zope.component.eventtesting
+optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
+
def setUpZope(test):
zope.component.eventtesting.setUp(test)
@@ -34,8 +36,7 @@
setUp=setUpZope,
tearDown=cleanUpZope,
checker=checker,
- optionflags=doctest.ELLIPSIS+
- doctest.NORMALIZE_WHITESPACE)
+ optionflags=optionflags)
suite.addTest(test)
return suite
@@ -44,4 +45,9 @@
suite = unittest.TestSuite()
for name in ['view', 'static', 'skin', 'template', 'directoryresource']:
suite.addTest(suiteFromPackage(name))
+ suite.addTest(doctest.DocFileSuite('../templatereg.txt',
+ optionflags=optionflags,
+ setUp=setUpZope,
+ tearDown=cleanUpZope,
+ ))
return suite
More information about the Checkins
mailing list