[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