[Checkins] SVN: CMF/branches/tseaver-pkg_resources/CMFCore/utils.py ImageResource as a replacement for ImageFile.

Tres Seaver tseaver at palladion.com
Mon Sep 4 11:54:54 EDT 2006


Log message for revision 69955:
  ImageResource as a replacement for ImageFile.

Changed:
  U   CMF/branches/tseaver-pkg_resources/CMFCore/utils.py

-=-
Modified: CMF/branches/tseaver-pkg_resources/CMFCore/utils.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/utils.py	2006-09-04 15:12:48 UTC (rev 69954)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/utils.py	2006-09-04 15:54:50 UTC (rev 69955)
@@ -30,13 +30,17 @@
 from Acquisition import aq_get
 from Acquisition import aq_inner
 from Acquisition import aq_parent
+from Acquisition import Explicit
 from Acquisition import Implicit
+from App.Common import rfc1123_date
 from DateTime import DateTime
 from ExtensionClass import Base
+from Globals import DevelopmentMode
 from Globals import HTMLFile
 from Globals import InitializeClass
 from Globals import MessageDialog
 from Globals import UNIQUE
+from OFS.content_types import guess_content_type
 from OFS.misc_ import misc_ as misc_images
 from OFS.misc_ import Misc_ as MiscImage
 from OFS.PropertyManager import PropertyManager
@@ -559,7 +563,8 @@
         except AttributeError:
             productObject = context.product
 
-        self.product_name = productObject.id
+        self.product_name = pid = productObject.id
+        productGlobals = context._ProductContext__pack.__dict__
         context.registerClass(
             meta_type = self.meta_type,
             # This is a little sneaky: we add self to the
@@ -568,16 +573,21 @@
             constructors = (manage_addToolForm,
                             manage_addTool,
                             self,),
-            icon = self.icon
+            #icon = self.icon
             )
-
+        # Lifted from ProductContext.registerClass, to avoid ImageFile
         if self.icon:
+            icon_res = ImageResource(self.icon, productGlobals)
+            icon_name = os_path.split(self.icon)[1]
+            if not hasattr(misc_images, pid):
+                setattr(misc_images, pid, MiscImage(pid, {}))
+            getattr(misc_images, pid)[icon_name] = icon_res
             icon = os_path.split(self.icon)[1]
-        else:
-            icon = None
+
         for tool in self.tools:
             tool.__factory_meta_type__ = self.meta_type
-            tool.icon = 'misc_/%s/%s' % (self.product_name, icon)
+            if self.icon:
+                tool.icon = 'misc_/%s/%s' % (self.product_name, icon_name)
 
 InitializeClass( ToolInit )
 
@@ -807,3 +817,130 @@
 
 security.declarePublic('MessageID')
 MessageID = MessageIDFactory('cmf_default')
+
+class ImageResource(Explicit):
+    """ Override ImageFile's insistence on reading the file eagerly.
+
+    o Also, make it work with pkg_resources.resource_stream.
+    """
+    _filepath = _package = _data = None
+    _fileIsRead = False
+
+    def __init__(self, path, _prefix):
+        """ Instantiate *without* reading the file.
+
+        o 'path' should normally be relative to the directory containing
+          the module instantiating us.
+
+        o '_prefix' should normally be the 'globals()' of the module
+          instantiating us.
+        """
+        if isinstance(_prefix, basestring):
+            self._filepath = os_path.join(_prefix, path)
+        else:
+            self._package = _prefix['__name__']
+            self._entry_subpath = path
+
+        self.__name__= os_path.split(path)[1]
+
+        if DevelopmentMode:
+            # In development mode, a shorter time is handy
+            max_age = 60 # One minute
+        else:
+            # A longer time reduces latency in production mode
+            max_age = 3600 # One hour
+
+        last_dot = path.rfind('.') + 1
+        # guess content type until we read the file
+        self._content_type = 'image/%s' % path[last_dot:]
+
+        self._last_modified = DateTime()
+        self._last_modified_str = rfc1123_date() # "now"
+        self._cache_control = 'public,max-age=%d' % max_age
+
+    def _fromDisk(self):
+
+        if not self._fileIsRead:
+            if self._package:
+                self._fileIsRead = self._asResource()
+            else:
+                self._fileIsRead = self._asAbsoluteFile()
+
+        if self._fileIsRead:
+            self._guessContentType()
+
+    def _asResource(self):
+        try:
+            from pkg_resources import resource_string
+        except ImportError: # waaa
+            module = sys.modules[self._package]
+            self._filepath = os_path.join(module.__file__, self._entry_subpath)
+            self._package = self._entry_subpath = None
+            return self._asAbsoluteFile()
+
+        try:
+            self._data = resource_string(self._package, self._entry_subpath)
+        except IOError:
+            return False
+
+    def _asAbsoluteFile(self):
+        try:
+            self._data = open(self._filepath, 'rb').read()
+        except IOError:
+            return False
+        return True
+
+    def _guessContentType(self):
+        content_type, enc = guess_content_type(
+                    self._filepath or self._entry_subpath, self._data)
+        if content_type:
+            self._content_type = content_type
+
+    __roles__ = None
+
+    def index_html(self, REQUEST, RESPONSE):
+        """ HTTP GET request
+        """
+        # HTTP If-Modified-Since header handling. This is duplicated
+        # from OFS.Image.Image - it really should be consolidated
+        # somewhere...
+        self._fromDisk()
+        RESPONSE.setHeader('Content-Type', self._content_type)
+        RESPONSE.setHeader('Last-Modified', self._last_modified_str)
+        RESPONSE.setHeader('Cache-Control', self._cache_control)
+
+        header = REQUEST.get_header('If-Modified-Since', None)
+
+        if header is not None:
+            header = header.split(';')[0]
+            # Some proxies seem to send invalid date strings for this
+            # header. If the date string is not valid, we ignore it
+            # rather than raise an error to be generally consistent
+            # with common servers such as Apache (which can usually
+            # understand the screwy date string as a lucky side effect
+            # of the way they parse it).
+            try:
+                mod_since = DateTime(header)
+            except:
+                pass
+            else:
+                if mod_since > self._last_modified:
+                    RESPONSE.setStatus(304)
+                    return ''
+
+        return self._data
+
+    HEAD__roles__=None
+    def HEAD(self, REQUEST, RESPONSE):
+        """ Process HTTP HEAD request.
+        """
+        RESPONSE.setHeader('Content-Type', self._content_type)
+        RESPONSE.setHeader('Last-Modified', self._last_modified_str)
+        return ''
+
+    def __len__(self):
+        # This is bogus and needed because of the way Python tests truth.
+        return 1
+
+    def __str__(self):
+        return '<img src="%s" alt="" />' % self.__name__



More information about the Checkins mailing list