[Checkins] SVN: grok/trunk/ Merge of templating branch!

Lennart Regebro regebro at gmail.com
Wed Nov 7 10:37:45 EST 2007


Log message for revision 81592:
  Merge of templating branch!
  

Changed:
  U   grok/trunk/CHANGES.txt
  U   grok/trunk/CREDITS.txt
  A   grok/trunk/doc/minitutorials/template-languages.txt
  U   grok/trunk/src/grok/__init__.py
  U   grok/trunk/src/grok/admin/view_templates/applications.pt
  U   grok/trunk/src/grok/admin/view_templates/docgrokclassview.pt
  U   grok/trunk/src/grok/admin/view_templates/docgrokgrokapplicationview.pt
  U   grok/trunk/src/grok/admin/view_templates/docgrokinterfaceview.pt
  U   grok/trunk/src/grok/admin/view_templates/docgrokmoduleview.pt
  U   grok/trunk/src/grok/admin/view_templates/docgrokpackageview.pt
  U   grok/trunk/src/grok/admin/view_templates/docgroktextfileview.pt
  U   grok/trunk/src/grok/admin/view_templates/docgrokview.pt
  U   grok/trunk/src/grok/admin/view_templates/index.pt
  U   grok/trunk/src/grok/admin/view_templates/inspect.pt
  U   grok/trunk/src/grok/admin/view_templates/server.pt
  U   grok/trunk/src/grok/components.py
  U   grok/trunk/src/grok/configure.zcml
  U   grok/trunk/src/grok/directive.py
  U   grok/trunk/src/grok/ftests/admin/macros.py
  U   grok/trunk/src/grok/interfaces.py
  U   grok/trunk/src/grok/meta.py
  U   grok/trunk/src/grok/templatereg.py
  U   grok/trunk/src/grok/tests/adapter/classorinterface.py
  A   grok/trunk/src/grok/tests/template/
  U   grok/trunk/src/grok/tests/template/__init__.py
  U   grok/trunk/src/grok/tests/template/pluggability.py
  U   grok/trunk/src/grok/tests/test_grok.py
  U   grok/trunk/src/grok/tests/utility/utility.py
  U   grok/trunk/src/grok/tests/view/dirandinlinetemplate.py
  U   grok/trunk/src/grok/tests/view/dirtemplate.py
  U   grok/trunk/src/grok/tests/view/dirtemplateandrender.py
  U   grok/trunk/src/grok/tests/view/dirtemplatesonly.py
  U   grok/trunk/src/grok/tests/view/templatedirectory.py
  U   grok/trunk/src/grok/tests/view/templatefile.py
  U   grok/trunk/src/grok/tests/view/templatereload.py
  U   grok/trunk/src/grok/tests/view/unassociated.py
  U   grok/trunk/src/grok/tests/zcml/directivepackage.py

-=-
Modified: grok/trunk/CHANGES.txt
===================================================================
--- grok/trunk/CHANGES.txt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/CHANGES.txt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -10,6 +10,19 @@
 * Integrated skins and layers: ``grok.layer``, ``grok.IGrokLayer``,
   ``grok.Skin``.
 
+* Grok now supports hooking in new template languages without much work.
+  See Restructuring below for more info.
+
+* Accessing a template macro via context/@@the_view/the_template is now 
+  deprecated for the standard ZPT story of using 
+  context/@@the_view/macro/the_template.
+
+* There is now a grok.direct() directive that can be used on GlobalUtilities
+  to mark that the class provides the utility interface directly and need 
+  no instantiation.
+  
+* Integrated skins and layers. grok.layer, grok.IGrokLayer, grok.Skin 
+
 * Removed ``grok.define_permission`` in favor of the
   ``grok.Permission`` component base class.  You should now subclass
   this base class to define permissions.
@@ -49,6 +62,25 @@
 * Grokkers now emit configuration actions, much like ZCML directive
   handlers do.
 
+* The new pluggable template language support includes some restructuring:
+  - GrokPageTemplate is now split up into two. BaseTemplate, on which all
+    templates need to be based, and GrokTemplate, which also provides a
+    set of methods for easy integration of templating languages.
+
+  - All objects based on GrokTemplate are now grokked, instead of having
+    separate grokkers for each type of template.
+
+  - The View is now completely template-language agnostic, which makes it 
+    easy to hook in new page template languages.
+  
+  - There are now new interfaces (ITemplate and ITemplateFileFactory)
+    used when you implement support for a new templating language.
+
+* The <grok:grok /> ZCML directive now emits a configuration action
+  instead of grokking right away at XML parsing time.  That way
+  grokkers can depend on components that are set up using regular ZCML
+  (e.g. checking for a permission that's defined in ZCML).
+
 * Changed the way grok's functional tests are set up.  Instead of each
   test case doing its own test setup, it is now done once by the
   ftesting layer.  This avoids ordering problems when some ftests

Modified: grok/trunk/CREDITS.txt
===================================================================
--- grok/trunk/CREDITS.txt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/CREDITS.txt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -31,6 +31,12 @@
 
 * Luciano Ramalho
 
+* Lennart Regebro (template pluggability)
+
+* Guido Wesdorp (template pluggability)
+
+* Brandon Rhodes (template pluggability)
+
 * ME GROK (team mascot)
 
 Thank you

Copied: grok/trunk/doc/minitutorials/template-languages.txt (from rev 81566, grok/branches/regebro-guido-templates/doc/minitutorials/template-languages.txt)
===================================================================
--- grok/trunk/doc/minitutorials/template-languages.txt	                        (rev 0)
+++ grok/trunk/doc/minitutorials/template-languages.txt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -0,0 +1,97 @@
+==================================
+Plugging in new template languages
+==================================
+
+:Author: Lennart Regebro
+
+Introduction
+------------
+
+Grok, like the Zope 3 framework on which it is built, uses Zope Page
+Templates as its default templating language.  While you can, of
+course, use whatever templating language you want in Grok by calling
+it manually, you can also “plug in” a template language such that both
+inline templates and templates stored in files are automatically
+linked with your Views — just like inline ``grok.PageTemplates`` and
+files with the ``.pt`` extension are by default.
+
+
+Inline templates
+----------------
+
+“Inline” templates are templates that you create right in your Python
+code — for example, by instantiating the default ``grok.PageTemplate``
+class with a literal string value as its argument.  Such templates are
+automatically associated with nearby ``View`` classes: if you create a
+View named ``Mammoth`` and next to it instantiate a template named
+``mammoth``, then Grok will use them together.
+
+To enable such automatic association for a new templating language, you need
+to write a subclass of ``grok.components.GrokTemplate``. You will need to
+override three methods. The ``setFromFilename`` and ``setFromString`` methods
+should each load the template from disk or a given string, depending on
+method. Your ``render`` method should run the template with the dictionary of
+values returned by ``self.getNamespace()`` and return the resulting string.
+
+Here is an example of a minimal page template integration:
+
+.. code-block:: python
+
+class MyPageTemplate(grok.components.GrokTemplate):
+
+    def setFromString(self, string):
+        self._template = MyTemplate(string)
+
+    def setFromFilename(self, filename, _prefix=None):
+        file = open(os.path.join(_prefix, filename))
+        self._template = MyTemplate(file.read())
+
+    def render(self, view):
+        return self._template.render(**self.getNamespace(view))
+
+With this class finished you can create an inline template, like this:
+
+.. code-block:: python
+
+    class AView(grok.View):
+        pass
+        
+    aview = MyPageTemplate('<html><body>Some text</body></html>')
+
+And also you can create a filebase template, inline:
+
+.. code-block:: python
+
+    class AView(grok.View):
+        pass
+        
+    aview = MyTemplateFile('lasceuax.html')
+
+
+Templates in the _templates directory
+-------------------------------------
+
+The most common use case, however, is to place templates for a file
+``foo.py`` in the corresponding ``foo_templates`` directory.  Grok, of
+course, already recognizes that files with a ``.pt`` extension each
+contain a Zope Page Template.  To tell Grok about a new file
+extension, simply register a global utility that generates a
+``MyPageTemplate`` when passed a filename.  That utility needs to
+implement the ``ITemplateFileFactory`` interface.
+
+.. code-block:: python
+
+class MyPageTemplateFactory(grok.GlobalUtility):
+
+    grok.implements(grok.interfaces.ITemplateFileFactory)
+    grok.name('mtl')
+
+    def __call__(self, filename, _prefix=None):
+        return MyPageTemplate(filename=filename, _prefix=_prefix)
+
+When your module gets grokked, Grok will discover the
+``MyPageTemplateFactory`` class, register it as a global utility for
+templates with the ``.mtl`` extension, and you can start creating
+``.mtl`` files in the template directory for your class.
+
+That's all you need!  Have fun!

Modified: grok/trunk/src/grok/__init__.py
===================================================================
--- grok/trunk/src/grok/__init__.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/__init__.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -42,7 +42,7 @@
 from grok.interfaces import IRESTSkinType
 from grok.directive import (context, name, title, template, templatedir,
                             provides, baseclass, global_utility, local_utility,
-                            permissions, require, site, layer)
+                            permissions, require, site, layer, direct)
 from grok.decorators import subscribe, adapter, implementer
 from martian.error import GrokError, GrokImportError
 

Modified: grok/trunk/src/grok/admin/view_templates/applications.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/applications.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/applications.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="context/@@grokadminmacros/gaia-page">
+<html metal:use-macro="context/@@grokadminmacros/macros/gaia-page">
   <div metal:fill-slot="content">
 
     <form tal:define="apps context/values"

Modified: grok/trunk/src/grok/admin/view_templates/docgrokclassview.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokclassview.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/docgrokclassview.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="view/app_root/@@grokadminmacros/gaia-page">
+<html metal:use-macro="view/app_root/@@grokadminmacros/macros/gaia-page">
   <head>
     <title>DocGrok page title</title>
   </head>

Modified: grok/trunk/src/grok/admin/view_templates/docgrokgrokapplicationview.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokgrokapplicationview.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/docgrokgrokapplicationview.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="view/app_root/@@grokadminmacros/gaia-page">
+<html metal:use-macro="view/app_root/@@grokadminmacros/macros/gaia-page">
   <head>
     <title>DocGrok page title</title>
   </head>

Modified: grok/trunk/src/grok/admin/view_templates/docgrokinterfaceview.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokinterfaceview.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/docgrokinterfaceview.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="view/app_root/@@grokadminmacros/gaia-page">
+<html metal:use-macro="view/app_root/@@grokadminmacros/macros/gaia-page">
   <head>
     <title>DocGrok page title</title>
   </head>

Modified: grok/trunk/src/grok/admin/view_templates/docgrokmoduleview.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokmoduleview.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/docgrokmoduleview.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="view/app_root/@@grokadminmacros/gaia-page">
+<html metal:use-macro="view/app_root/@@grokadminmacros/macros/gaia-page">
   <head>
     <title>DocGrok page title</title>
   </head>

Modified: grok/trunk/src/grok/admin/view_templates/docgrokpackageview.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokpackageview.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/docgrokpackageview.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="view/app_root/@@grokadminmacros/gaia-page">
+<html metal:use-macro="view/app_root/@@grokadminmacros/macros/gaia-page">
   <head>
     <title>DocGrokPackage page title</title>
   </head>

Modified: grok/trunk/src/grok/admin/view_templates/docgroktextfileview.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgroktextfileview.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/docgroktextfileview.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="view/app_root/@@grokadminmacros/gaia-page">
+<html metal:use-macro="view/app_root/@@grokadminmacros/macros/gaia-page">
   <head>
     <title>DocGrok page title</title>
   </head>

Modified: grok/trunk/src/grok/admin/view_templates/docgrokview.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/docgrokview.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/docgrokview.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="view/app_root/@@grokadminmacros/gaia-page">
+<html metal:use-macro="view/app_root/@@grokadminmacros/macros/gaia-page">
   <head>
     <title>DocGrok page title</title>
   </head>

Modified: grok/trunk/src/grok/admin/view_templates/index.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/index.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/index.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="context/@@grokadminmacros/gaia-page">
+<html metal:use-macro="context/@@grokadminmacros/macros/gaia-page">
   <div metal:fill-slot="content">
     <h1></h1>
     <div class="logo">

Modified: grok/trunk/src/grok/admin/view_templates/inspect.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/inspect.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/inspect.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="context/@@grokadminmacros/gaia-page">
+<html metal:use-macro="context/@@grokadminmacros/macros/gaia-page">
   <div metal:fill-slot="content">
     <h1>
       <span tal:content="view/info/name" />

Modified: grok/trunk/src/grok/admin/view_templates/server.pt
===================================================================
--- grok/trunk/src/grok/admin/view_templates/server.pt	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/admin/view_templates/server.pt	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,4 +1,4 @@
-<html metal:use-macro="context/@@grokadminmacros/gaia-page">
+<html metal:use-macro="context/@@grokadminmacros/macros/gaia-page">
   <div metal:fill-slot="content">
     <h1>Manage your Zope 3 instance</h1>
 

Modified: grok/trunk/src/grok/components.py
===================================================================
--- grok/trunk/src/grok/components.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/components.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -13,6 +13,7 @@
 ##############################################################################
 """Grok components"""
 
+import sys
 import os
 import persistent
 import urllib
@@ -137,18 +138,23 @@
         return mapply(self.render, (), self.request)
 
     def _render_template(self):
-        namespace = self.template.pt_getContext()
-        namespace['request'] = self.request
-        namespace['view'] = self
-        namespace['context'] = self.context
-        # XXX need to check whether we really want to put None here if missing
-        namespace['static'] = self.static
-        return self.template.pt_render(namespace)
+        return self.template.render(self)
 
+    def namespace(self):
+        return {}
+
     def __getitem__(self, key):
-        # XXX give nice error message if template is None
-        return self.template.macros[key]
-
+        # This is BBB code for Zope page templates only:
+        if not isinstance(self.template, PageTemplate):
+            raise AttributeError("View has no item %s" % key)
+        # When this deprecation is done with, this whole __getitem__ can 
+        # be removed.
+        warnings.warn("Calling macros directly on the view is deprecated. "
+                      "Please use view/@@viewname/macros/macroname\n"
+                      "View %r, macro %s" % (self, key),
+                      DeprecationWarning)
+        return self.template._template.macros[key]
+    
     def url(self, obj=None, name=None):
         # if the first argument is a string, that's the name. There should
         # be no second argument
@@ -224,8 +230,14 @@
         return simplejson.dumps(method_result)
 
 
-class GrokPageTemplate(object):
+class BaseTemplate(object):
+    """Any sort of page template"""
+    
+    interface.implements(interfaces.ITemplate)
 
+    __grok_name__ = ''
+    __grok_location__ = ''
+
     def __repr__(self):
         return '<%s template in %s>' % (self.__grok_name__,
                                         self.__grok_location__)
@@ -234,38 +246,99 @@
         self.__grok_name__ = name
         self.__grok_location__ = location
 
+    def _initFactory(self, factory):
+        pass
 
-class PageTemplate(GrokPageTemplate, TrustedAppPT, pagetemplate.PageTemplate):
-    expand = 0
 
-    def __init__(self, template):
-        super(PageTemplate, self).__init__()
-        if martian.util.not_unicode_or_ascii(template):
-            raise ValueError("Invalid page template. Page templates must be "
-                             "unicode or ASCII.")
-        self.write(template)
+class GrokTemplate(BaseTemplate):
+    """A slightly more advanced page template
+    
+    This provides most of what a page template needs and is a good base for
+    writing your own page template"""
+    
+    def __init__(self, string=None, filename=None, _prefix=None):
 
         # __grok_module__ is needed to make defined_locally() return True for
         # inline templates
-        # XXX unfortunately using caller_module means that
-        # PageTemplate cannot be subclassed
+        # XXX unfortunately using caller_module means that care must be taken
+        # when GrokTemplate is subclassed. You can not do a super().__init__
+        # for example.
         self.__grok_module__ = martian.util.caller_module()
+        
+        if not (string is None) ^ (filename is None):
+            raise AssertionError("You must pass in template or filename, but not both.")
+        
+        if string:
+            self.setFromString(string)
+        else:
+            if _prefix is None:
+                module = sys.modules[self.__grok_module__]
+                _prefix = os.path.dirname(module.__file__)
+            self.setFromFilename(filename, _prefix)
 
+    def __repr__(self):
+        return '<%s template in %s>' % (self.__grok_name__,
+                                        self.__grok_location__)
+    
+    def _annotateGrokInfo(self, name, location):
+        self.__grok_name__ = name
+        self.__grok_location__ = location
 
-class PageTemplateFile(GrokPageTemplate, TrustedAppPT,
-                       pagetemplatefile.PageTemplateFile):
+    def _initFactory(self, factory):
+        pass
 
-    def __init__(self, filename, _prefix=None):
-        _prefix = self.get_path_from_prefix(_prefix)
-        super(PageTemplateFile, self).__init__(filename, _prefix)
+    def namespace(self, view):
+        namespace = {}
+        namespace['request'] = view.request
+        namespace['view'] = view
+        namespace['context'] = view.context
+        # XXX need to check whether we really want to put None here if missing
+        namespace['static'] = view.static
+        
+        return namespace
+    
+    def getNamespace(self, view):
+        namespace = self.namespace(view)
+        namespace.update(view.namespace())
+        return namespace
 
-        # __grok_module__ is needed to make defined_locally() return True for
-        # inline templates
-        # XXX unfortunately using caller_module means that
-        # PageTemplateFile cannot be subclassed
-        self.__grok_module__ = martian.util.caller_module()
+class TrustedPageTemplate(TrustedAppPT, pagetemplate.PageTemplate):
+    pass
 
+class TrustedFilePageTemplate(TrustedAppPT, pagetemplatefile.PageTemplateFile):
+    pass
 
+class PageTemplate(GrokTemplate):
+    
+    def setFromString(self, string):
+        zpt = TrustedPageTemplate()
+        if martian.util.not_unicode_or_ascii(string):
+            raise ValueError("Invalid page template. Page templates must be "
+                             "unicode or ASCII.")
+        zpt.write(string)
+        self._template = zpt
+
+    def setFromFilename(self, filename, _prefix=None):
+        self._template = TrustedFilePageTemplate(filename, _prefix)
+
+    def _initFactory(self, factory):
+        factory.macros = self._template.macros
+
+    def render(self, view):
+        namespace = self.getNamespace(view)
+        template = self._template
+        namespace.update(template.pt_getContext())
+        return template.pt_render(namespace)
+    
+class PageTemplateFile(PageTemplate):
+    # For BBB
+    def __init__(self, filename, _prefix=None):
+        self.__grok_module__ = martian.util.caller_module()
+        if _prefix is None:
+            module = sys.modules[self.__grok_module__]
+            _prefix = os.path.dirname(module.__file__)
+        self.setFromFilename(filename, _prefix)
+    
 class DirectoryResource(directoryresource.DirectoryResource):
     # We subclass this, because we want to override the default factories for
     # the resources so that .pt and .html do not get created as page

Modified: grok/trunk/src/grok/configure.zcml
===================================================================
--- grok/trunk/src/grok/configure.zcml	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/configure.zcml	2007-11-07 15:37:45 UTC (rev 81592)
@@ -81,6 +81,10 @@
   <!-- need to grok this for some basic REST support -->
   <grok:grok package=".rest" />
 
+  <!-- ZPT support -->
+  <grok:grok package=".templatereg" />
+  
+  <!-- The admin interface -->
   <grok:grok package=".admin" />
 
 </configure>

Modified: grok/trunk/src/grok/directive.py
===================================================================
--- grok/trunk/src/grok/directive.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/directive.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -41,8 +41,10 @@
 
 
 class GlobalUtilityInfo(object):
-    def __init__(self, factory, provides=None, name=u'', direct=False):
+    def __init__(self, factory, provides=None, name=u'', direct=None):
         self.factory = factory
+        if direct is None:
+            direct = util.class_annotation(factory, 'grok.direct', False)
         self.direct = direct
 
         if provides is None:
@@ -122,3 +124,4 @@
     'grok.permissions', ClassDirectiveContext())
 layer = InterfaceOrClassDirective('grok.layer',
                            ClassOrModuleDirectiveContext())
+direct = MarkerDirective('grok.direct', ClassDirectiveContext())
\ No newline at end of file

Modified: grok/trunk/src/grok/ftests/admin/macros.py
===================================================================
--- grok/trunk/src/grok/ftests/admin/macros.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/ftests/admin/macros.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -62,7 +62,7 @@
     grok.context(Mammoth)
 
 externalview = grok.PageTemplate("""\
-<html metal:use-macro="context/@@grokadminmacros/gaia-page">
+<html metal:use-macro="context/@@grokadminmacros/macros/gaia-page">
 </html>
 """)
 

Modified: grok/trunk/src/grok/interfaces.py
===================================================================
--- grok/trunk/src/grok/interfaces.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/interfaces.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -445,3 +445,24 @@
 class IRESTSkinType(IInterface):
     """Skin type for REST requests.
     """
+
+class ITemplateFileFactory(interface.Interface):
+    """Utility that generates templates from files in template directories. 
+    """
+    
+    def __call__(filename, _prefix=None):
+        """Creates an ITemplate from a file
+        
+        _prefix is the directory the file is located in
+        """
+
+class ITemplate(interface.Interface):
+    """Template objects
+    """
+    
+    def _initFactory(factory):
+        """Template language specific initializations on the view factory."""
+        
+    def render(view):
+        """Renders the template"""
+

Modified: grok/trunk/src/grok/meta.py
===================================================================
--- grok/trunk/src/grok/meta.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/meta.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -103,16 +103,23 @@
 class GlobalUtilityGrokker(martian.ClassGrokker):
     component_class = grok.GlobalUtility
 
+    # This needs to happen before the FilesystemPageTemplateGrokker grokker 
+    # happens, since it relies on the ITemplateFileFactories being grokked.
+    priority = 1100
+
     def grok(self, name, factory, module_info, config, **kw):
         provides = util.class_annotation(factory, 'grok.provides', None)
         if provides is None:
             util.check_implements_one(factory)
         name = util.class_annotation(factory, 'grok.name', '')
+        direct = util.class_annotation(factory, 'grok.direct', False)
+        if not direct:
+            factory = factory()
 
         config.action(
             discriminator=('utility', provides, name),
             callable=component.provideUtility,
-            args=(factory(), provides, name),
+            args=(factory, provides, name),
             )
         return True
 
@@ -277,7 +284,10 @@
 
         return True
 
+def view__getitem__(self, key):
+    return self.template.macros[key]
 
+
 class JSONGrokker(martian.ClassGrokker):
     component_class = grok.JSON
 
@@ -350,7 +360,7 @@
     # use the templates
     priority = 1000
 
-    component_class = grok.PageTemplate
+    component_class = grok.components.BaseTemplate
 
     def grok(self, name, instance, module_info, config, **kw):
         templates = module_info.getAnnotation('grok.templates', None)
@@ -367,12 +377,8 @@
             args=(name, module_info.dotted_name)
         )
         return True
+        
 
-
-class ModulePageTemplateFileGrokker(ModulePageTemplateGrokker):
-    component_class = grok.PageTemplateFile
-
-
 class FilesystemPageTemplateGrokker(martian.GlobalGrokker):
     # do this early on, but after ModulePageTemplateGrokker, as
     # findFilesystem depends on module-level templates to be

Modified: grok/trunk/src/grok/templatereg.py
===================================================================
--- grok/trunk/src/grok/templatereg.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/templatereg.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -2,6 +2,7 @@
 from martian import util
 
 import os
+import zope.component
 import grok
 import warnings
 
@@ -35,7 +36,13 @@
             if template_file.startswith('.') or template_file.endswith('~'):
                 continue
 
-            if not template_file.endswith('.pt'):
+            template_name, extension = os.path.splitext(template_file)
+            extension = extension[1:] # Get rid of the leading dot.
+            template_factory = zope.component.queryUtility(
+                grok.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.
@@ -44,18 +51,19 @@
                               (template_file, template_dir), UserWarning, 2)
                 continue
 
-            template_name = template_file[:-3] # cut off .pt
-            template = grok.PageTemplateFile(template_file, template_dir)
-            template_path = os.path.join(template_dir, template_file)
-            template._annotateGrokInfo(template_name, template_path)
-
             inline_template = self.get(template_name)
             if inline_template:
                 raise GrokError("Conflicting templates found for name '%s' "
-                                "in module %r, both inline and in template "
-                                "directory '%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 = template_factory(template_file, template_dir)
+            template_path = os.path.join(template_dir, template_file)
+            template._annotateGrokInfo(template_name, template_path)
+
             self.register(template_name, template)
 
     def listUnassociated(self):
@@ -97,8 +105,17 @@
                     "an associated template." % factory, factory)
             self.markAssociated(template_name)
             factory.template = template
+            template._initFactory(factory)
         else:
             if not getattr(factory, 'render', None):
                 # we do not accept a view without any way to render it
                 raise GrokError("View %r has no associated template or "
                                 "'render' method." % factory, factory)
+
+class PageTemplateFileFactory(grok.GlobalUtility):
+    
+    grok.implements(grok.interfaces.ITemplateFileFactory)
+    grok.name('pt')
+    
+    def __call__(self, filename, _prefix=None):
+        return grok.components.PageTemplate(filename=filename, _prefix=_prefix)

Modified: grok/trunk/src/grok/tests/adapter/classorinterface.py
===================================================================
--- grok/trunk/src/grok/tests/adapter/classorinterface.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/adapter/classorinterface.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -7,7 +7,7 @@
     ...
   GrokImportError: You can only pass classes or interfaces to grok.context.
 
-  >>> string_context()
+  >>> stringcontext()
   Traceback (most recent call last):
     ...
   GrokImportError: You can only pass classes or interfaces to grok.context.
@@ -32,7 +32,7 @@
     class FunctionContext(object):
         grok.context(a)
 
-def string_context():
+def stringcontext():
     class StringContext(object):
         grok.context('string')
 

Copied: grok/trunk/src/grok/tests/template (from rev 81566, grok/branches/regebro-guido-templates/src/grok/tests/template)

Modified: grok/trunk/src/grok/tests/template/__init__.py
===================================================================
--- grok/branches/regebro-guido-templates/src/grok/tests/template/__init__.py	2007-11-06 14:13:52 UTC (rev 81566)
+++ grok/trunk/src/grok/tests/template/__init__.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1 +1 @@
-#
\ No newline at end of file
+#

Modified: grok/trunk/src/grok/tests/template/pluggability.py
===================================================================
--- grok/branches/regebro-guido-templates/src/grok/tests/template/pluggability.py	2007-11-06 14:13:52 UTC (rev 81566)
+++ grok/trunk/src/grok/tests/template/pluggability.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -65,8 +65,11 @@
     def __call__(self, filename, _prefix=None):
         return MyPageTemplate(filename=filename, _prefix=_prefix)
 
+#import pdb
+#pdb.set_trace()
+
 # Small hack to make sure this gets grokked in the right order:
-grok.grok_component('MyPageTemplateFactory', MyPageTemplateFactory)
+#grok.grok_component('MyPageTemplateFactory', MyPageTemplateFactory)
 
 class Cave(grok.Model):
     pass

Modified: grok/trunk/src/grok/tests/test_grok.py
===================================================================
--- grok/trunk/src/grok/tests/test_grok.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/test_grok.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -35,7 +35,8 @@
                                     tearDown=cleanUpZope,
                                     checker=checker,
                                     optionflags=doctest.ELLIPSIS+
-                                    doctest.NORMALIZE_WHITESPACE)
+                                    doctest.NORMALIZE_WHITESPACE+
+                                    doctest.REPORT_ONLY_FIRST_FAILURE)
 
         suite.addTest(test)
     return suite
@@ -45,7 +46,7 @@
     for name in ['adapter', 'error', 'view', 'event', 'security', 'catalog',
                  'zcml', 'static', 'utility', 'xmlrpc', 'json', 'container',
                  'traversal', 'form', 'grokker', 'directive', 'util',
-                 'baseclass', 'annotation', 'application']:
+                 'baseclass', 'annotation', 'application', 'template']:
         suite.addTest(suiteFromPackage(name))
     return suite
 

Modified: grok/trunk/src/grok/tests/utility/utility.py
===================================================================
--- grok/trunk/src/grok/tests/utility/utility.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/utility/utility.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -39,6 +39,16 @@
   >>> isinstance(nightclub, NightClub)
   True
 
+You can make the class the utility by providing the grok.direct() directive,
+if you also use interface.classProvides instead of grok.provides.
+This is useful for utilities that do nothing but create instances:
+
+  >>> clubmaker = component.getUtility(IClubMaker, 'maker')
+  >>> IClubMaker.providedBy(clubmaker)
+  True
+  >>> clubmaker is ClubMaker
+  True
+
 Utilities (including classes that do not subclass from grok.GlobalUtility) can
 be (re-)registered using grok.global_utility:
 
@@ -133,6 +143,9 @@
 class INightClub(interface.Interface):
     pass
 
+class IClubMaker(interface.Interface):
+    pass
+
 class NormalClub(grok.GlobalUtility):
     grok.implements(IClub)
 
@@ -154,6 +167,12 @@
     grok.provides(ISmallClub)
     grok.name('tiny')
 
+class ClubMaker(grok.GlobalUtility):
+    grok.implements(IClub)
+    interface.classProvides(IClubMaker)
+    grok.direct()
+    grok.name('maker')
+
 class IFireplace(interface.Interface):
     pass
 

Modified: grok/trunk/src/grok/tests/view/dirandinlinetemplate.py
===================================================================
--- grok/trunk/src/grok/tests/view/dirandinlinetemplate.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/view/dirandinlinetemplate.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -2,13 +2,12 @@
 If multiple templates can be found, one in the module and one in the
 template directory, there is an error:
 
+  >>> grok.testing.grok('grok.templatereg')
   >>> grok.testing.grok(__name__)
   Traceback (most recent call last):
     ...
   ConfigurationExecutionError: martian.error.GrokError: Conflicting templates found for name 'cavepainting' in module
-  <module 'grok.tests.view.dirandinlinetemplate' from '...'>,
-  both inline and in template directory '...dirandinlinetemplate_templates'.
-  in:
+  <module 'grok.tests.view.dirandinlinetemplate' from ...
 
 """
 import grok

Modified: grok/trunk/src/grok/tests/view/dirtemplate.py
===================================================================
--- grok/trunk/src/grok/tests/view/dirtemplate.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/view/dirtemplate.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,6 +1,7 @@
 """
 Templates can also be found in a directory with the same name as the module:
 
+  >>> grok.testing.grok('grok.templatereg')
   >>> grok.testing.grok(__name__)
   
   >>> manfred = Mammoth()

Modified: grok/trunk/src/grok/tests/view/dirtemplateandrender.py
===================================================================
--- grok/trunk/src/grok/tests/view/dirtemplateandrender.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/view/dirtemplateandrender.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -2,6 +2,7 @@
 A View may either have an associated template or a render-method. Here
 we check that this also works for templates in a template-directory:
 
+  >>> grok.testing.grok('grok.templatereg')
   >>> grok.testing.grok(__name__)
   Traceback (most recent call last):
     ...

Modified: grok/trunk/src/grok/tests/view/dirtemplatesonly.py
===================================================================
--- grok/trunk/src/grok/tests/view/dirtemplatesonly.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/view/dirtemplatesonly.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -6,6 +6,7 @@
   >>> warnings.warn = warn
 
 
+  >>> grok.testing.grok('grok.templatereg')
   >>> grok.testing.grok(__name__)
     From tests.py's showwarning():
     ... UserWarning: File 'invalid.txt' has an unrecognized extension in directory '...dirtemplatesonly_templates'...

Modified: grok/trunk/src/grok/tests/view/templatedirectory.py
===================================================================
--- grok/trunk/src/grok/tests/view/templatedirectory.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/view/templatedirectory.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -1,6 +1,7 @@
 """
 You can explicitly specify the template directory using grok.templatedir on module level:
 
+  >>> grok.testing.grok('grok.templatereg')
   >>> grok.testing.grok(__name__)
 
   >>> manfred = Mammoth()

Modified: grok/trunk/src/grok/tests/view/templatefile.py
===================================================================
--- grok/trunk/src/grok/tests/view/templatefile.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/view/templatefile.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -27,5 +27,5 @@
 class Food(grok.View):
     pass
 
-food = grok.PageTemplateFile(os.path.join('templatedirectoryname',
-                                          'food.pt'))
+food = grok.PageTemplate(filename=os.path.join('templatedirectoryname',
+                                               'food.pt'))

Modified: grok/trunk/src/grok/tests/view/templatereload.py
===================================================================
--- grok/trunk/src/grok/tests/view/templatereload.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/view/templatereload.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -2,6 +2,7 @@
 Filesystem-based templates, once grokked, can be changed.  The change
 will automatically be picked up, reloading Zope is not necessary.
 
+  >>> grok.testing.grok('grok.templatereg')
   >>> grok.testing.grok(__name__)
   >>> from zope.component import getMultiAdapter
   >>> from zope.publisher.browser import TestRequest

Modified: grok/trunk/src/grok/tests/view/unassociated.py
===================================================================
--- grok/trunk/src/grok/tests/view/unassociated.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/view/unassociated.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -2,6 +2,7 @@
 Templates that are not associated with a view class will provoke an
 error:
 
+  >>> grok.testing.grok('grok.templatereg')
   >>> grok.testing.grok(__name__)
   Traceback (most recent call last):
   ...

Modified: grok/trunk/src/grok/tests/zcml/directivepackage.py
===================================================================
--- grok/trunk/src/grok/tests/zcml/directivepackage.py	2007-11-07 15:37:21 UTC (rev 81591)
+++ grok/trunk/src/grok/tests/zcml/directivepackage.py	2007-11-07 15:37:45 UTC (rev 81592)
@@ -3,6 +3,9 @@
   >>> from zope.configuration import xmlconfig
   >>> context = xmlconfig.file('meta.zcml', grok)
 
+Load the ZPT factory
+  >>> grok.testing.grok('grok.templatereg')
+
   >>> ignored = xmlconfig.string('''
   ... <configure
   ...     xmlns="http://namespaces.zope.org/zope"



More information about the Checkins mailing list