[Checkins] SVN: Sandbox/nadako/zope.browserresource/ Add pluggable file resource factories depending on file extension. The implementation is based on the patch attached to this report - https://bugs.launchpad.net/zope3/+bug/98459. This should be a step to moving PageTemplate resources into a separate package.

Dan Korostelev nadako at gmail.com
Sun Aug 23 12:05:54 EDT 2009


Log message for revision 103110:
  Add pluggable file resource factories depending on file extension. The implementation is based on the patch attached to this report - https://bugs.launchpad.net/zope3/+bug/98459. This should be a step to moving PageTemplate resources into a separate package.

Changed:
  U   Sandbox/nadako/zope.browserresource/CHANGES.txt
  U   Sandbox/nadako/zope.browserresource/TODO.txt
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/configure.zcml
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/directoryresource.py
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/fileresource.py
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/i18nfileresource.py
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/icon.py
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/interfaces.py
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/metaconfigure.py
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/metadirectives.py
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/pagetemplateresource.py
  A   Sandbox/nadako/zope.browserresource/src/zope/browserresource/resourcefactories.zcml
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/tests/test_directives.py
  U   Sandbox/nadako/zope.browserresource/src/zope/browserresource/tests/test_directoryresource.py

-=-
Modified: Sandbox/nadako/zope.browserresource/CHANGES.txt
===================================================================
--- Sandbox/nadako/zope.browserresource/CHANGES.txt	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/CHANGES.txt	2009-08-23 16:05:54 UTC (rev 103110)
@@ -10,6 +10,20 @@
 
 Additional changes that are made during refactoring:
 
+ * Resource class for file resources are now selected the pluggable way.
+   The resource directory publisher and browser:resource ZCML directive
+   now creating file resources using factory utility lookup based on the
+   file extension, so it's now possible to add new resource types without
+   introducing new ZCML directives and they will work inside resource
+   directories as well.
+   
+   NOTE: the "resource_factories" attribute from the DirectoryResource
+   was removed, so if you were using this attribute for changing resource
+   classes for some file extensions, you need to migrate your code to new
+   utility-based mechanism.
+
+   See zope.browserresource.interfaces.IResourceFactoryFactory interface.
+
  * Fix stripping the "I" from an interface name for icon title, if no
    title is specified.
 

Modified: Sandbox/nadako/zope.browserresource/TODO.txt
===================================================================
--- Sandbox/nadako/zope.browserresource/TODO.txt	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/TODO.txt	2009-08-23 16:05:54 UTC (rev 103110)
@@ -1,6 +1,4 @@
  * Clean up tests, possibly move them to doctests.
  * Simplify the overall mechanism, remove extra entities.
- * Implement pluggable resource factories for resource
-   directory (see https://bugs.launchpad.net/zope3/+bug/98459).
  * Move PageTemplateResource to a separate package, so this
    one could be independent on any templating system.

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/configure.zcml
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/configure.zcml	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/configure.zcml	2009-08-23 16:05:54 UTC (rev 103110)
@@ -31,4 +31,6 @@
     <allow attributes="get __getitem__" />
   </class>
 
+  <include file="resourcefactories.zcml" />
+
 </configure>

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/directoryresource.py
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/directoryresource.py	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/directoryresource.py	2009-08-23 16:05:54 UTC (rev 103110)
@@ -25,16 +25,17 @@
 """
 import os
 
-from zope.interface import implements
+from zope.component import queryUtility
+from zope.interface import implements, classProvides
 from zope.publisher.browser import BrowserView
 from zope.publisher.interfaces import NotFound
 from zope.publisher.interfaces.browser import IBrowserPublisher
 
 from zope.browserresource.fileresource import FileResourceFactory
-from zope.browserresource.fileresource import ImageResourceFactory
-from zope.browserresource.pagetemplateresource import PageTemplateResourceFactory
 from zope.browserresource.resource import Resource
 from zope.browserresource.resources import empty
+from zope.browserresource.interfaces import IResourceFactory
+from zope.browserresource.interfaces import IResourceFactoryFactory
 
 _marker = object()
 
@@ -50,15 +51,6 @@
 
     implements(IBrowserPublisher)
 
-    resource_factories = {
-        '.gif':  ImageResourceFactory,
-        '.png':  ImageResourceFactory,
-        '.jpg':  ImageResourceFactory,
-        '.pt':   PageTemplateResourceFactory,
-        '.zpt':  PageTemplateResourceFactory,
-        '.html': PageTemplateResourceFactory,
-        }
-
     default_factory = FileResourceFactory
     directory_factory = None
 
@@ -88,8 +80,9 @@
             return default
 
         if isfile:
-            ext = os.path.splitext(os.path.normcase(name))[1]
-            factory = self.resource_factories.get(ext, self.default_factory)
+            ext = os.path.splitext(os.path.normcase(name))[1][1:]
+            factory = queryUtility(IResourceFactoryFactory, ext,
+                                   self.default_factory)
         else:
             factory = self.directory_factory
 
@@ -101,6 +94,9 @@
 
 class DirectoryResourceFactory(object):
 
+    implements(IResourceFactory)
+    classProvides(IResourceFactoryFactory)
+
     factoryClass = DirectoryResource
 
     def __init__(self, path, checker, name):
@@ -114,5 +110,4 @@
         resource.__name__ = self.__name
         return resource
 
-
 DirectoryResource.directory_factory = DirectoryResourceFactory

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/fileresource.py
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/fileresource.py	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/fileresource.py	2009-08-23 16:05:54 UTC (rev 103110)
@@ -25,12 +25,14 @@
 
 from zope.contenttype import guess_content_type
 from zope.datetime import time as timeFromDateTimeString
-from zope.interface import implements
+from zope.interface import implements, classProvides
 from zope.publisher.browser import BrowserView
 from zope.publisher.interfaces import NotFound
 from zope.publisher.interfaces.browser import IBrowserPublisher
 
 from zope.browserresource.resource import Resource
+from zope.browserresource.interfaces import IResourceFactory
+from zope.browserresource.interfaces import IResourceFactoryFactory
 
 
 class File(object):
@@ -157,6 +159,9 @@
 
     resourceClass = FileResource
 
+    implements(IResourceFactory)
+    classProvides(IResourceFactoryFactory)
+
     def __init__(self, path, checker, name):
         self.__file = File(path, name)
         self.__checker = checker
@@ -172,6 +177,9 @@
 class ImageResourceFactory(object):
 
     resourceClass = FileResource
+    
+    implements(IResourceFactory)
+    classProvides(IResourceFactoryFactory)
 
     def __init__(self, path, checker, name):
         self.__file = Image(path, name)

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/i18nfileresource.py
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/i18nfileresource.py	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/i18nfileresource.py	2009-08-23 16:05:54 UTC (rev 103110)
@@ -17,9 +17,11 @@
 """
 from zope.i18n.interfaces import II18nAware
 from zope.i18n.negotiator import negotiator
-from zope.interface import implements
+from zope.interface import implements, classProvides
 
 from zope.browserresource.fileresource import FileResource
+from zope.browserresource.interfaces import IResourceFactory
+from zope.browserresource.interfaces import IResourceFactoryFactory
 
 
 class I18nFileResource(FileResource):
@@ -79,6 +81,9 @@
 
 class I18nFileResourceFactory(object):
 
+    implements(IResourceFactory)
+    classProvides(IResourceFactoryFactory)
+
     def __init__(self, data, defaultLanguage):
         self.__data = data
         self.__defaultLanguage = defaultLanguage

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/icon.py
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/icon.py	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/icon.py	2009-08-23 16:05:54 UTC (rev 103110)
@@ -35,8 +35,7 @@
         # The context is important here, since it becomes the parent of the
         # icon, which is needed to generate the absolute URL.
         resource = getResource(self.context, self.rname, self.request)
-        src = resource()
-        return src
+        return resource()
 
 class IconViewFactory(object):
 

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/interfaces.py
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/interfaces.py	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/interfaces.py	2009-08-23 16:05:54 UTC (rev 103110)
@@ -24,3 +24,23 @@
 
     def __call__():
         """return the absolute URL of this resource."""
+
+class IResourceFactory(Interface):
+    
+    def __call__(request):
+        pass
+
+class IResourceFactoryFactory(Interface):
+    """A factory for IResourceFactory objects
+    
+    These factories are registered as named utilities that can be selected
+    for creating resource factories in a pluggable way.
+    
+    Resource directories and browser:resource directive use these utilities
+    to choose what resource to create, depending on the file extension, so
+    third-party packages could easily plug-in additional resource types.
+    
+    """
+    
+    def __call__(path, checker, name):
+        """Return an IResourceFactory"""

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/metaconfigure.py
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/metaconfigure.py	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/metaconfigure.py	2009-08-23 16:05:54 UTC (rev 103110)
@@ -17,10 +17,11 @@
 """
 import os
 
+from zope.component import queryUtility
 from zope.component.interface import provideInterface
 from zope.component.zcml import handler
 from zope.configuration.exceptions import ConfigurationError
-from zope.interface import Interface
+from zope.interface import Interface, implements, classProvides
 from zope.publisher.interfaces.browser import IBrowserRequest
 from zope.publisher.interfaces.browser import IDefaultBrowserLayer
 from zope.security.checker import CheckerPublic, NamesChecker, Checker
@@ -33,12 +34,17 @@
 from zope.browserresource.i18nfileresource import I18nFileResourceFactory
 from zope.browserresource.pagetemplateresource import PageTemplateResourceFactory
 from zope.browserresource.icon import IconViewFactory
+from zope.browserresource.interfaces import IResourceFactory
+from zope.browserresource.interfaces import IResourceFactoryFactory
 
 allowed_names = ('GET', 'HEAD', 'publishTraverse', 'browserDefault',
                  'request', '__call__')
 
 class ResourceFactoryWrapper(object):
 
+    implements(IResourceFactory)
+    classProvides(IResourceFactoryFactory)
+
     def __init__(self, factory, checker, name):
         self.__factory = factory
         self.__checker = checker
@@ -69,21 +75,27 @@
             " attributes for resource directives"
             )
 
+    _context.action(
+        discriminator = ('resource', name, IBrowserRequest, layer),
+        callable = resourceHandler,
+        args = (name, layer, checker, factory, file, image, template, _context.info),
+        )
+
+
+def resourceHandler(name, layer, checker, factory, file, image, template, context_info):
     if factory is not None:
         factory = ResourceFactoryWrapper(factory, checker, name)
     elif file:
-        factory = FileResourceFactory(file, checker, name)
+        ext = os.path.splitext(os.path.normcase(file))[1][1:]
+        factory_factory = queryUtility(IResourceFactoryFactory, ext,
+                                       FileResourceFactory)
+        factory = factory_factory(file, checker, name)
     elif image:
         factory = ImageResourceFactory(image, checker, name)
     else:
         factory = PageTemplateResourceFactory(template, checker, name)
 
-    _context.action(
-        discriminator = ('resource', name, IBrowserRequest, layer),
-        callable = handler,
-        args = ('registerAdapter',
-                factory, (layer,), Interface, name, _context.info),
-        )
+    handler('registerAdapter', factory, (layer,), Interface, name, context_info)
 
 
 def resourceDirectory(_context, name, directory, layer=IDefaultBrowserLayer,

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/metadirectives.py
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/metadirectives.py	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/metadirectives.py	2009-08-23 16:05:54 UTC (rev 103110)
@@ -72,7 +72,12 @@
 
     file = Path(
         title=u"File",
-        description=u"The file containing the resource data.",
+        description=u"The file containing the resource data. The resource "
+                    u"type that will be created depends on file extension. "
+                    u"The named IResourceFactoryFactory utilities are "
+                    u"registered per extension. If no factory is registered "
+                    u"for given file extension, the default FileResource "
+                    u"factory will be used.",
         required=False
         )
 

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/pagetemplateresource.py
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/pagetemplateresource.py	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/pagetemplateresource.py	2009-08-23 16:05:54 UTC (rev 103110)
@@ -16,7 +16,7 @@
 $Id$
 """
 
-from zope.interface import implements
+from zope.interface import implements, classProvides
 from zope.pagetemplate.engine import TrustedAppPT
 from zope.pagetemplate.pagetemplatefile import PageTemplateFile
 from zope.publisher.browser import BrowserView
@@ -24,6 +24,8 @@
 from zope.publisher.interfaces.browser import IBrowserPublisher
 
 from zope.browserresource.resource import Resource
+from zope.browserresource.interfaces import IResourceFactory
+from zope.browserresource.interfaces import IResourceFactoryFactory
 
 class PageTemplate(TrustedAppPT, PageTemplateFile):
     """
@@ -70,6 +72,9 @@
 
 class PageTemplateResourceFactory(object):
 
+    implements(IResourceFactory)
+    classProvides(IResourceFactoryFactory)
+
     def __init__(self, path, checker, name):
         self.__pt = PageTemplate(path)
         self.__checker = checker

Added: Sandbox/nadako/zope.browserresource/src/zope/browserresource/resourcefactories.zcml
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/resourcefactories.zcml	                        (rev 0)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/resourcefactories.zcml	2009-08-23 16:05:54 UTC (rev 103110)
@@ -0,0 +1,39 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+  <utility
+      name="gif"
+      component=".fileresource.ImageResourceFactory"
+      provides=".interfaces.IResourceFactoryFactory"
+      />
+  
+  <utility
+      name="png"
+      component=".fileresource.ImageResourceFactory"
+      provides=".interfaces.IResourceFactoryFactory"
+      />
+  
+  <utility
+      name="jpg"
+      component=".fileresource.ImageResourceFactory"
+      provides=".interfaces.IResourceFactoryFactory"
+      />
+  
+  <utility
+      name="pt"
+      component=".pagetemplateresource.PageTemplateResourceFactory"
+      provides=".interfaces.IResourceFactoryFactory"
+      />
+  
+  <utility
+      name="zpt"
+      component=".pagetemplateresource.PageTemplateResourceFactory"
+      provides=".interfaces.IResourceFactoryFactory"
+      />
+  
+  <utility
+      name="html"
+      component=".pagetemplateresource.PageTemplateResourceFactory"
+      provides=".interfaces.IResourceFactoryFactory"
+      />
+
+</configure>

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/tests/test_directives.py
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/tests/test_directives.py	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/tests/test_directives.py	2009-08-23 16:05:54 UTC (rev 103110)
@@ -42,7 +42,7 @@
 import zope.publisher.defaultview
 import zope.browserresource
 from zope.component import provideAdapter, provideUtility
-from zope.component.testfiles.views import IC, V1, VZMI, R1, IV
+from zope.component.testfiles.views import R1, IV
 from zope.browserresource.fileresource import FileResource
 from zope.browserresource.i18nfileresource import I18nFileResource
 from zope.testing import cleanup
@@ -61,44 +61,6 @@
 
 request = TestRequest()
 
-class V2(V1, object):
-
-    def action(self):
-        return self.action2()
-
-    def action2(self):
-        return "done"
-
-class VT(V1, object):
-    def publishTraverse(self, request, name):
-        try:
-            return int(name)
-        except:
-            return super(VT, self).publishTraverse(request, name)
-
-class Ob(object):
-    implements(IC)
-
-ob = Ob()
-
-class NCV(object):
-    "non callable view"
-
-    def __init__(self, context, request):
-        pass
-
-class CV(NCV):
-    "callable view"
-    def __call__(self):
-        pass
-
-
-class C_w_implements(NCV):
-    implements(Interface)
-
-    def index(self):
-        return self
-
 class ITestLayer(IBrowserRequest):
     """Test Layer."""
 

Modified: Sandbox/nadako/zope.browserresource/src/zope/browserresource/tests/test_directoryresource.py
===================================================================
--- Sandbox/nadako/zope.browserresource/src/zope/browserresource/tests/test_directoryresource.py	2009-08-23 15:29:26 UTC (rev 103109)
+++ Sandbox/nadako/zope.browserresource/src/zope/browserresource/tests/test_directoryresource.py	2009-08-23 16:05:54 UTC (rev 103110)
@@ -28,6 +28,7 @@
 from zope.traversing.browser.absoluteurl import AbsoluteURL
 from zope.traversing.browser.interfaces import IAbsoluteURL
 from zope.component import provideAdapter
+from zope.configuration.xmlconfig import XMLConfig
 
 from zope.testing import cleanup
 
@@ -56,6 +57,10 @@
     def setUp(self):
         super(Test, self).setUp()
         provideAdapter(AbsoluteURL, (None, None), IAbsoluteURL)
+        import zope.browserresource
+        import zope.component
+        XMLConfig('meta.zcml', zope.component)()
+        XMLConfig('resourcefactories.zcml', zope.browserresource)()
 
     def testNotFound(self):
         path = os.path.join(test_directory, 'testfiles')



More information about the Checkins mailing list