[CMF-checkins] SVN: CMF/branches/tseaver-pkg_resources/CMFCore/ - Checkpoint.

Tres Seaver tseaver at palladion.com
Tue Oct 25 18:38:06 EDT 2005


Log message for revision 39626:
   - Checkpoint.

Changed:
  U   CMF/branches/tseaver-pkg_resources/CMFCore/DirectoryView.py
  U   CMF/branches/tseaver-pkg_resources/CMFCore/FSMetadata.py
  U   CMF/branches/tseaver-pkg_resources/CMFCore/FSObject.py
  U   CMF/branches/tseaver-pkg_resources/CMFCore/FSPageTemplate.py
  U   CMF/branches/tseaver-pkg_resources/CMFCore/tests/base/testcase.py
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/not_a_package/
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/not_a_package/rotten.egg
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/__init__.py
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/rotten/
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/rotten/rotten.pt
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/rotten/rotten.pt.metadata
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/stinky.py
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/__init__.py
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/aargh/
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/aargh/__init__.py
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/aargh/data.txt
  A   CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/setup.py
  U   CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_DirectoryView.py
  U   CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_FSMetadata.py

-=-
Modified: CMF/branches/tseaver-pkg_resources/CMFCore/DirectoryView.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/DirectoryView.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/DirectoryView.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -17,6 +17,9 @@
 
 import re
 from os import path, listdir, stat
+from pkg_resources import resource_listdir
+from pkg_resources import resource_isdir
+from pkg_resources import resource_string
 from sys import exc_info
 from sys import platform
 
@@ -31,6 +34,7 @@
 from OFS.Folder import Folder
 from OFS.ObjectManager import bad_id
 from zLOG import LOG, ERROR
+from zope.interface import Interface
 
 from FSMetadata import FSMetadata
 from FSObject import BadFile
@@ -73,24 +77,29 @@
                     for name in names ]
         listdir.extend(results)
 
-class DirectoryInformation:
+class IDirectoryInformation(Interface):
+
+    def getSubdirs():
+        """ Return a sequence of names of subdirs within this directory.
+
+        o Excludes subdirs which match 'ignore'.
+        """
+
+    def reload():
+        """ Remove all cached information about the directory.
+        """
+
+    def readFile(self, filename, mode='r'):
+        """ Return the data from the given file.
+
+        o Return None if the file does not exist.
+        """
+
+class DirectoryInformationBase:
+
     data = None
-    _v_last_read = 0
-    _v_last_filelist = [] # Only used on Win32
+    subdirs = ()
 
-    def __init__(self, filepath, minimal_fp, ignore=ignore):
-        self._filepath = filepath
-        self._minimal_fp = minimal_fp
-        self.ignore=base_ignore + tuple(ignore)
-        if platform == 'win32':
-            self._walker = _walker(self.ignore)
-        subdirs = []
-        for entry in _filtered_listdir(self._filepath, ignore=self.ignore):
-           entry_filepath = path.join(self._filepath, entry)
-           if path.isdir(entry_filepath):
-               subdirs.append(entry)
-        self.subdirs = tuple(subdirs)
-
     def getSubdirs(self):
         return self.subdirs
 
@@ -108,14 +117,9 @@
         """ Read the .objects file produced by FSDump.
         """
         types = {}
-        try:
-            f = open( path.join(self._filepath, '.objects'), 'rt' )
-        except IOError:
-            pass
-        else:
-            lines = f.readlines()
-            f.close()
-            for line in lines:
+        data = self.readFile('.objects', 'rt')
+        if data is not None:
+            for line in data.splitlines():
                 try:
                     obname, meta_type = line.split(':')
                 except ValueError:
@@ -124,6 +128,56 @@
                     types[obname.strip()] = meta_type.strip()
         return types
 
+    def _changed(self):
+        return False
+
+    def getContents(self, registry):
+        changed = self._changed()
+        if self.data is None or changed:
+            try:
+                self.data, self.objects = self.prepareContents(registry,
+                    register_subdirs=changed)
+            except:
+                LOG('DirectoryView',
+                    ERROR,
+                    'Error during prepareContents:',
+                    error=exc_info())
+                self.data = {}
+                self.objects = ()
+
+        return self.data, self.objects
+
+class DirectoryInformation(DirectoryInformationBase):
+
+    _v_last_read = 0
+    _v_last_filelist = [] # Only used on Win32
+
+    def __init__(self, filepath, minimal_fp, ignore=ignore):
+        self._filepath = filepath
+        self._minimal_fp = minimal_fp
+        self.ignore = base_ignore + tuple(ignore)
+        if platform == 'win32':
+            self._walker = _walker(self.ignore)
+        subdirs = []
+        for entry in _filtered_listdir(self._filepath, ignore=self.ignore):
+           entry_filepath = path.join(self._filepath, entry)
+           if path.isdir(entry_filepath):
+               subdirs.append(entry)
+        self.subdirs = tuple(subdirs)
+
+    def readFile(self, filename, mode='r'):
+        data = None
+        try:
+            f = open( path.join(self._filepath, filename), mode )
+        except IOError:
+            pass
+        else:
+            try:
+                data = f.read()
+            finally:
+                f.close()
+        return data
+
     if DevelopmentMode:
 
         def _changed(self):
@@ -152,27 +206,6 @@
 
             return 0
 
-    else:
-
-        def _changed(self):
-            return 0
-
-    def getContents(self, registry):
-        changed = self._changed()
-        if self.data is None or changed:
-            try:
-                self.data, self.objects = self.prepareContents(registry,
-                    register_subdirs=changed)
-            except:
-                LOG('DirectoryView',
-                    ERROR,
-                    'Error during prepareContents:',
-                    error=exc_info())
-                self.data = {}
-                self.objects = ()
-
-        return self.data, self.objects
-
     def prepareContents(self, registry, register_subdirs=0):
         # Creates objects for each file.
         data = {}
@@ -203,7 +236,7 @@
                     t = registry.getTypeByMetaType(mt)
                     if t is None:
                         t = DirectoryView
-                    metadata = FSMetadata(entry_filepath)
+                    metadata = FSMetadata(filename=entry_filepath)
                     metadata.read()
                     ob = t( entry
                           , entry_minimal_fp
@@ -237,7 +270,7 @@
                     t = registry.getTypeByExtension(ext)
 
                 if t is not None:
-                    metadata = FSMetadata(entry_filepath)
+                    metadata = FSMetadata(filename=entry_filepath)
                     metadata.read()
                     try:
                         ob = t(name, entry_minimal_fp, fullname=entry,
@@ -288,7 +321,169 @@
 
         return data, tuple(objects)
 
+class ResourceDirectoryInformation(DirectoryInformationBase):
+    """ DI for directories read using pkg_resources API.
+    """
 
+    def __init__(self, pname, name, ignore=ignore):
+        self._pname = pname
+        self._name = name
+        self.ignore = base_ignore + tuple(ignore)
+        subdirs = []
+        for entry in self._listEntries():
+            entry_subpath = self._getEntrySubpath(entry)
+            if resource_isdir(pname, entry_subpath):
+               subdirs.append(entry)
+        self.subdirs = tuple(subdirs)
+
+    def _listEntries(self):
+        for entry in resource_listdir(self._pname, self._name):
+            if entry not in self.ignore:
+                yield entry
+
+    def _getEntrySubpath(self, entry):
+        return '%s/%s' % (self._name, entry)
+
+    def readFile(self, filename, mode='ignored'):
+        try:
+            return resource_string(self._pname, '%s/%s' % (self._name,
+                                                           filename))
+        except IOError:
+            return None
+
+    def prepareContents(self, registry, register_subdirs=0):
+        # Creates objects for each file.
+        data = {}
+        objects = []
+        pname = self._pname
+        types = self._readTypesFile()
+        faux_path_prefix = pname.split('.')
+
+        for entry in resource_listdir(self._pname, self._name):
+
+            if not self._isAllowableFilename(entry):
+                continue
+
+            entry_subpath = self._getEntrySubpath(entry)
+            faux_path_elements = faux_path_prefix + [entry]
+            faux_path = '/'.join(faux_path_elements)
+
+            if entry in self.subdirs:
+                # Add a subdirectory only if it was previously registered,
+                # unless register_subdirs is set.
+                info = registry.getDirectoryInfo(faux_path)
+                if info is None and register_subdirs:
+                    # Register unknown subdirs
+                    registry.registerDirectoryByGlobals(entry_subpath, 
+                                                        {'__name__' :
+                                                            self._pname },
+                                                        ignore=self.ignore,
+                                                       )
+                    info = registry.getDirectoryInfo(faux_path)
+                if info is not None:
+                    # Folders on the file system have no extension or 
+                    # meta_type, as a crutch to enable customizing what gets
+                    # created to represent a filesystem folder in a 
+                    # DirectoryView we use a fake type "FOLDER". That way
+                    # other implementations can register for that type and
+                    # circumvent the hardcoded assumption that all filesystem
+                    # directories will turn into DirectoryViews.
+                    mt = types.get(entry) or 'FOLDER'
+                    t = registry.getTypeByMetaType(mt)
+                    if t is None:
+                        t = DirectoryView
+                    metadata = FSMetadata(package=pname,
+                                          entry_subpath=entry_subpath)
+                    metadata.read()
+                    ob = t( entry
+                          , entry_minimal_fp
+                          , properties=metadata.getProperties()
+                          )
+                    ob_id = ob.getId()
+                    data[ob_id] = ob
+                    objects.append({'id': ob_id, 'meta_type': ob.meta_type})
+            else:
+                pos = entry.rfind('.')
+                if pos >= 0:
+                    name = entry[:pos]
+                    ext = path.normcase(entry[pos + 1:])
+                else:
+                    name = entry
+                    ext = ''
+                if not name or name == 'REQUEST':
+                    # Not an allowable id.
+                    continue
+                mo = bad_id(name)
+                if mo is not None and mo != -1:  # Both re and regex formats
+                    # Not an allowable id.
+                    continue
+                t = None
+                mt = types.get(entry, None)
+                if mt is None:
+                    mt = types.get(name, None)
+                if mt is not None:
+                    t = registry.getTypeByMetaType(mt)
+                if t is None:
+                    t = registry.getTypeByExtension(ext)
+
+                if t is not None:
+                    metadata = FSMetadata(package=pname,
+                                          entry_subpath=entry_subpath)
+                    metadata.read()
+                    try:
+                        ob = t(name,
+                               package=pname,
+                               entry_subpath=entry_subpath,
+                               fullname=entry,
+                               properties=metadata.getProperties(),
+                              )
+                    except:
+                        import traceback
+                        typ, val, tb = exc_info()
+                        try:
+                            exc_lines = traceback.format_exception( typ,
+                                                                    val,
+                                                                    tb )
+                            LOG( 'DirectoryView', ERROR,
+                                 '\n'.join(exc_lines) )
+
+                            ob = BadFile( name,
+                                          entry_minimal_fp,
+                                          exc_str='\r\n'.join(exc_lines),
+                                          fullname=entry )
+                        finally:
+                            tb = None   # Avoid leaking frame!
+
+                    # FS-based security
+                    permissions = metadata.getSecurity()
+                    if permissions is not None:
+                        for name in permissions.keys():
+                            acquire, roles = permissions[name]
+                            try:
+                                ob.manage_permission(name,roles,acquire)
+                            except ValueError:
+                                LOG('DirectoryView',
+                                    ERROR,
+                                    'Error setting permissions',
+                                    error=exc_info())
+
+                    # only DTML Methods and Python Scripts can have proxy roles
+                    if hasattr(ob, '_proxy_roles'):
+                        try:
+                            ob._proxy_roles = tuple(metadata.getProxyRoles())
+                        except:
+                            LOG('DirectoryView',
+                                ERROR,
+                                'Error setting proxy role',
+                                error=exc_info())
+
+                    ob_id = ob.getId()
+                    data[ob_id] = ob
+                    objects.append({'id': ob_id, 'meta_type': ob.meta_type})
+
+        return data, tuple(objects)
+
+
 class DirectoryRegistry:
 
     def __init__(self):
@@ -311,11 +506,32 @@
     def registerDirectory(self, name, _prefix, subdirs=1, ignore=ignore):
         # This what is actually called to register a
         # file system directory to become a FSDV.
-        if not isinstance(_prefix, basestring):
-            _prefix = package_home(_prefix)
-        filepath = path.join(_prefix, name)
-        self.registerDirectoryByPath(filepath, subdirs, ignore=ignore)
+        if isinstance(_prefix, basestring):
+            filepath = path.join(_prefix, name)
+            self.registerDirectoryByPath(filepath, subdirs, ignore=ignore)
+        else:
+            self.registerDirectoryByGlobals(name, _prefix, subdirs, ignore)
 
+    def registerDirectoryByGlobals(self, name, pglobals,
+                                   subdirs=1, ignore=ignore):
+        pname = pglobals['__name__']
+        faux_path_elements = pname.split('.')
+        faux_path_elements.append(name)
+        faux_path = '/'.join(faux_path_elements)
+        info = ResourceDirectoryInformation(pname, name, ignore=ignore)
+        self._directories[faux_path] = info
+        if subdirs:
+            for entry in resource_listdir(pname, name):
+                if entry in ignore:
+                    continue
+                sub_name = '%s/%s' % (name, entry)
+                if resource_isdir(pname, sub_name):
+                    self.registerDirectoryByGlobals( sub_name
+                                                   , pglobals
+                                                   , subdirs
+                                                   , ignore=ignore
+                                                   )
+
     def registerDirectoryByPath(self, filepath, subdirs=1, ignore=ignore):
         # This is indirectly called during registration of
         # a directory. As you can see, minimalpath is called
@@ -532,16 +748,22 @@
     still needs to be called by product initialization code to satisfy
     persistence demands.
     """
-    if not isinstance(_prefix, basestring):
-        _prefix = package_home(_prefix)
-    filepath = path.join(_prefix, name)
-    minimal_fp = minimalpath(filepath)
-    info = _dirreg.getDirectoryInfo(minimal_fp)
+    if isinstance(_prefix, basestring):
+        filepath = path.join(_prefix, name)
+        adjusted = minimalpath(filepath)
+    else:
+        pname = _prefix['__name__']
+        elements = pname.split('.') + [name]
+        adjusted = '/'.join(elements)
+
+    info = _dirreg.getDirectoryInfo(adjusted)
+
     if info is None:
         raise ValueError('Not a registered directory: %s' % minimal_fp)
+
     for entry in info.getSubdirs():
-        entry_minimal_fp = '/'.join( (minimal_fp, entry) )
-        createDirectoryView(ob, entry_minimal_fp, entry)
+        entry_subname = '%s/%s' % (adjusted, entry)
+        createDirectoryView(ob, entry_subname, entry)
 
 def manage_addDirectoryView(self, dirpath, id=None, REQUEST=None):
     """ Add either a DirectoryView or a derivative object.

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/FSMetadata.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/FSMetadata.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/FSMetadata.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -15,13 +15,13 @@
 $Id$
 """
 
-from zLOG import LOG, ERROR
-from sys import exc_info
-from os.path import exists
 from ConfigParser import ConfigParser
+from pkg_resources import resource_stream
+import re
+import sys
 from warnings import warn
 
-import re
+from zLOG import LOG, ERROR
 
 class CMFConfigParser(ConfigParser):
     """ This our wrapper around ConfigParser to
@@ -44,20 +44,30 @@
 
 class FSMetadata:
     # public API
-    def __init__(self, filename):
-        self._filename = filename
+    _package = None
+    _entry_subpath = None
+    _filename = None
+    def __init__(self, package=None, entry_subpath=None, filename=None):
+        MESSAGE = ("Either 'filename' or 'package' + 'entry_subpath' must "
+                   "be supplied.")
+        if filename is None:
+            if package is None or entry_subpath is None:
+                raise ValueError(MESSAGE)
+            self._package = package
+            self._entry_subpath = entry_subpath
+        else:
+            if package is not None or entry_subpath is not None:
+                raise ValueError(MESSAGE)
+            self._filename = filename
 
     def read(self):
-        """ Find the files to read, either the old security and
-        properties type or the new metadata type """
-        filename = self._filename + '.metadata'
-        if exists(filename):
-            # found the new type, lets use that
-            self._readMetadata()
-        else:
-            # not found so try the old ones
-            self._properties = self._old_readProperties()
-            self._security = self._old_readSecurity()
+        """ Find the file(s) and read them.
+        
+        o Prefer new '.metdata' file.
+        
+        o Fall back to '.properties' and '.security' files.
+        """
+        self._readMetadata()
 
     def getProxyRoles(self):
         """ Returns the proxy roles """
@@ -82,24 +92,46 @@
         self._security = {}
 
         try:
+            stream = self._getStream()
+            if stream is None:
+                return
+
             cfg = CMFConfigParser()
-            cfg.read(self._filename + '.metadata')
+            cfg.readfp(stream)
 
             # the two sections we care about
-            self._properties = self._getSectionDict(cfg, 'default')
-            self._security = self._getSectionDict(cfg, 'security',
+            self._properties = self._getSectionDict(cfg, 'Default')
+            self._security = self._getSectionDict(cfg, 'Security',
                                                   self._securityParser)
         except:
             LOG('FSMetadata',
                  ERROR,
                 'Error parsing .metadata file',
-                 error=exc_info())
+                 error=sys.exc_info())
 
         # to add in a new value such as proxy roles,
         # just add in the section, call it using getSectionDict
         # if you need a special parser for some whacky
         # config, then just pass through a special parser
 
+
+    def _getStream(self):
+        """ Return a stream opened against our .metadata file
+        
+        o File may be found either via 'pkg_resoruces' or via direct
+          file access.
+        """
+        if self._package is not None:
+            try:
+                filename = '%s%s' % (self._entry_subpath, '.metadata')
+                return resource_stream(self._package, filename)
+            except IOError:
+                return None
+        try:
+            return open(self._filename + '.metadata', 'r')
+        except IOError:
+            return None
+            
     def _nullParser(self, data):
         """
         This is the standard rather boring null parser that does very little
@@ -145,72 +177,3 @@
         # we need to return None if we have none to be compatible
         # with existing API
         return None
-
-    def _old_readProperties(self):
-        """
-        Reads the properties file next to an object.
-
-        Moved from DirectoryView.py to here with only minor
-        modifications. Old and deprecated in favour of .metadata now
-        """
-        fp = self._filename + '.properties'
-        try:
-            f = open(fp, 'rt')
-        except IOError:
-            return None
-        else:
-            warn('.properties objects will disappear in CMF 1.7 - Use '
-                 '.metadata objects instead.', DeprecationWarning)
-            lines = f.readlines()
-            f.close()
-            props = {}
-            for line in lines:
-                kv = line.split('=', 1)
-                if len(kv) == 2:
-                    props[kv[0].strip()] = kv[1].strip()
-                else:
-                    LOG('FSMetadata',
-                        ERROR,
-                        'Error parsing .properties file',
-                        error=exc_info())
-
-            return props
-
-    def _old_readSecurity(self):
-        """
-        Reads the security file next to an object.
-
-        Moved from DirectoryView.py to here with only minor
-        modifications. Old and deprecated in favour of .metadata now
-        """
-        fp = self._filename + '.security'
-        try:
-            f = open(fp, 'rt')
-        except IOError:
-            return None
-        else:
-            warn('.security objects will disappear in CMF 1.7 - Use '
-                 '.metadata objects instead.', DeprecationWarning)
-            lines = f.readlines()
-            f.close()
-            prm = {}
-            for line in lines:
-                try:
-                    c1 = line.index(':')+1
-                    c2 = line.index(':',c1)
-                    permission = line[:c1-1]
-                    acquire = bool(line[c1:c2])
-                    proles = line[c2+1:].split(',')
-                    roles=[]
-                    for role in proles:
-                        role = role.strip()
-                        if role:
-                            roles.append(role)
-                except:
-                    LOG('DirectoryView',
-                        ERROR,
-                        'Error reading permission from .security file',
-                        error=exc_info())
-                        # warning use of exc_info is deprecated
-                prm[permission]=(acquire,roles)
-            return prm

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/FSObject.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/FSObject.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/FSObject.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -51,10 +51,31 @@
     security = ClassSecurityInfo()
     security.declareObjectProtected(View)
 
+    _filepath = None
+    _package = None
+    _entry_subpath = None
     _file_mod_time = 0
     _parsed = 0
 
-    def __init__(self, id, filepath, fullname=None, properties=None):
+    def __init__(self, id, filepath=None, package=None, entry_subpath=None,
+                 fullname=None, properties=None):
+        MESSAGE = ("Either 'filepath' or 'package' + 'entry_subpath' must "
+                   "be supplied.")
+        if filepath is None:
+            if package is None or entry_subpath is None:
+                raise ValueError(MESSAGE)
+            self._package = package
+            self._entry_subpath = entry_subpath
+        else:
+            if package is not None or entry_subpath is not None:
+                raise ValueError(MESSAGE)
+            self._filepath = filepath
+            fp = expandpath(self._filepath)
+            try:
+                self._file_mod_time = stat(fp)[8]
+            except:
+                pass
+
         if properties:
             # Since props come from the filesystem, this should be
             # safe.
@@ -68,11 +89,6 @@
 
         self.id = id
         self.__name__ = id # __name__ is used in traceback reporting
-        self._filepath = filepath
-        fp = expandpath(self._filepath)
-
-        try: self._file_mod_time = stat(fp)[8]
-        except: pass
         self._readFile(0)
 
     security.declareProtected(ViewManagementScreens, 'manage_doCustomize')

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/FSPageTemplate.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/FSPageTemplate.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/FSPageTemplate.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -15,6 +15,7 @@
 $Id$
 """
 
+from pkg_resources import resource_string
 import re
 
 import Globals
@@ -63,8 +64,10 @@
     # Declare security for unprotected PageTemplate methods.
     security.declarePrivate('pt_edit', 'write')
 
-    def __init__(self, id, filepath, fullname=None, properties=None):
-        FSObject.__init__(self, id, filepath, fullname, properties)
+    def __init__(self, id, filepath=None, package=None, entry_subpath=None,
+                 fullname=None, properties=None):
+        FSObject.__init__(self, id, filepath, package, entry_subpath,
+                          fullname, properties)
         self.ZBindings_edit(self._default_bindings)
 
     def _createZODBClone(self):
@@ -78,12 +81,15 @@
 #        return 0
 
     def _readFile(self, reparse):
-        fp = expandpath(self._filepath)
-        file = open(fp, 'r')    # not 'rb', as this is a text file!
-        try:
-            data = file.read()
-        finally:
-            file.close()
+        if self._filepath is None:
+            data = resource_string(self._package, self._entry_subpath)
+        else:
+            fp = expandpath(self._filepath)
+            file = open(fp, 'r')    # not 'rb', as this is a text file!
+            try:
+                data = file.read()
+            finally:
+                file.close()
 
         if reparse:
             # If we already have a content_type set it must come from a
@@ -100,7 +106,7 @@
                     encoding = xml_info.group(1) or 'utf-8'
                     self.content_type = 'text/xml; charset=%s' % encoding
 
-            self.write(data)
+        self.write(data)
 
     security.declarePrivate('read')
     def read(self):

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/tests/base/testcase.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/tests/base/testcase.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/tests/base/testcase.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -227,3 +227,50 @@
         # kill the copy
         self._free_warning_output()
         rmtree(self.tempname)
+
+class EggTestsBase( FSDVTest ):
+    """ Base class for tests of skins inside .egg files.
+    """
+    _skinname = 'not_a_package'
+
+    def setUp(self):
+        import os
+        from pkg_resources import working_set
+        import sys
+        import Products
+        FSDVTest.setUp(self)
+        self._entry_name = os.path.join(self.tempname, self._skinname)
+        self._oldpath = sys.path[:]
+        self._oldmodules = {}
+        self._oldmodules.update(sys.modules)
+        self._saveWorkingSet()
+        self._products_path = Products.__path__[:]
+        working_set.entries.insert(0, self._entry_name)
+        sys.path.insert(0, os.path.join(self.tempname,
+                                        self._skinname,
+                                       ))
+
+    def tearDown(self):
+        import sys
+        from pkg_resources import working_set
+        import Products
+        sys.path[:] = self._oldpath
+        sys.modules.clear()
+        sys.modules.update(self._oldmodules)
+        Products.__path__[:] = self._products_path
+        self._restoreWorkingSet()
+        FSDVTest.tearDown(self)
+
+    def _saveWorkingSet(self):
+        from pkg_resources import working_set
+        self._ws_entries = working_set.entries[:]
+        self._ws_entry_keys = dict(working_set.entry_keys)
+        self._ws_by_key = dict(working_set.by_key)
+
+    def _restoreWorkingSet(self):
+        from pkg_resources import working_set
+        working_set.entries[:] = self._ws_entries
+        working_set.entry_keys.clear()
+        working_set.entry_keys.update(self._ws_entry_keys)
+        working_set.by_key.clear() 
+        working_set.by_key.update(self._ws_by_key)

Added: CMF/branches/tseaver-pkg_resources/CMFCore/tests/not_a_package/rotten.egg
===================================================================
(Binary files differ)


Property changes on: CMF/branches/tseaver-pkg_resources/CMFCore/tests/not_a_package/rotten.egg
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/__init__.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/__init__.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/__init__.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -0,0 +1,7 @@
+""" Rotten product (demo CMF eggification)
+
+$Id$
+"""
+
+def initialize(context):
+    pass


Property changes on: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/rotten/rotten.pt
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/rotten/rotten.pt	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/rotten/rotten.pt	2005-10-25 22:38:06 UTC (rev 39626)
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h1> Rotten to the Core </h1>
+</body>
+</html>


Property changes on: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/rotten/rotten.pt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/rotten/rotten.pt.metadata
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/rotten/rotten.pt.metadata	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/skins/rotten/rotten.pt.metadata	2005-10-25 22:38:06 UTC (rev 39626)
@@ -0,0 +1,7 @@
+[Default]
+title=Rotten Template
+proxy=Foo
+
+[Security]
+Access contents information = 1:Manager,Anonymous
+View management screens = 0:Manager

Added: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/stinky.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/stinky.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/stinky.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -0,0 +1,26 @@
+""" Rotten content object.
+
+$Id$
+"""
+from pkg_resources import resource_string
+
+from AccessControl.SecurityInfo import ClassSecurityInfo
+from Globals import InitializeClass
+from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
+
+from Products.CMFCore.URLTool import URLTool
+
+class Stinky(URLTool):
+    """
+    """
+    security = ClassSecurityInfo()
+
+    manage_tabs = ({'label' : 'Stench', 'action' : 'manage_stench'},
+                  ) + URLTool.manage_options
+
+    manage_stench = ZopePageTemplate('manage_stench',
+                                     resource_string(__name__,
+                                                     'zmi/stench.pt'),
+                                    )
+
+InitializeClass(Stinky)


Property changes on: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/Rotten/stinky.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/__init__.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/__init__.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/__init__.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -0,0 +1 @@
+# Namespace package


Property changes on: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/Products/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/aargh/__init__.py
===================================================================


Property changes on: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/aargh/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/aargh/data.txt
===================================================================


Property changes on: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/aargh/data.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/setup.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/setup.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/setup.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -0,0 +1,14 @@
+from setuptools import find_packages
+from setuptools import setup
+
+setup(
+    name='rotten',
+    version='0.1',
+    packages=find_packages(),
+    namespace_packages=['Products'],
+    package_data={'': ['*.txt'],
+                  'Products.Rotten': ['skins/rotten/*',
+                                      'profiles/rotten/*',
+                                     ],
+                 },
+)


Property changes on: CMF/branches/tseaver-pkg_resources/CMFCore/tests/rotten.egg/setup.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_DirectoryView.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_DirectoryView.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_DirectoryView.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -11,6 +11,7 @@
 
 from Products.CMFCore.tests.base.dummy import DummyFolder
 from Products.CMFCore.tests.base.testcase import _prefix
+from Products.CMFCore.tests.base.testcase import EggTestsBase
 from Products.CMFCore.tests.base.testcase import FSDVTest
 from Products.CMFCore.tests.base.testcase import WarningInterceptor
 
@@ -259,7 +260,35 @@
         testfolder = self.ob.fake_skin.test_directory
         self.failUnless(isinstance(testfolder, DummyDirectoryViewSurrogate))
 
+class DirectoryViewEggTests( EggTestsBase ):
 
+    def test_resource_listdir_works_with_egg(self):
+        from pkg_resources import Requirement
+        from pkg_resources import require
+        from pkg_resources import resource_listdir
+
+        require('rotten')
+        import Products.Rotten
+        found = resource_listdir('Products.Rotten', 'skins/rotten')
+        self.assertEqual(len(found), 1, found)
+        self.assertEqual(found[0], 'rotten.pt')
+
+    def test_registerDirectory_in_egg(self):
+        from pkg_resources import require
+        require('rotten')
+        from Products.CMFCore.DirectoryView import registerDirectory
+        from Products.CMFCore.DirectoryView import _dirreg
+
+        before = _dirreg._directories.copy()
+        registerDirectory('skins', {'__name__': 'Products.Rotten'})
+        after = _dirreg._directories.copy()
+
+        added = [x for x in after.keys() if x not in before]
+        self.assertEqual(len(added), 2)
+        self.failUnless('Products/Rotten/skins' in added)
+        self.failUnless('Products/Rotten/skins/rotten' in added)
+
+
 if DevelopmentMode:
 
   class DebugModeTests( FSDVTest ):
@@ -337,6 +366,7 @@
         makeSuite(DirectoryViewTests),
         makeSuite(DirectoryViewIgnoreTests),
         makeSuite(DirectoryViewFolderTests),
+        makeSuite(DirectoryViewEggTests),
         makeSuite(DebugModeTests),
         ))
 

Modified: CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_FSMetadata.py
===================================================================
--- CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_FSMetadata.py	2005-10-25 22:08:56 UTC (rev 39625)
+++ CMF/branches/tseaver-pkg_resources/CMFCore/tests/test_FSMetadata.py	2005-10-25 22:38:06 UTC (rev 39626)
@@ -20,9 +20,11 @@
 Zope2.startup()
 
 from test_FSSecurity import FSSecurityBase
+from base.testcase import EggTestsBase
+from base.dummy import DummyFolder
 
 
-class FSMetadata(FSSecurityBase):
+class FSMetadataDirectoryTests(FSSecurityBase):
 
     def _checkProxyRoles(self, obj, roles):
         # Test proxy roles on the object
@@ -84,9 +86,30 @@
         self._checkProxyRoles(ob, ['Manager', 'Anonymous'])
 
 
+class FSMetadataEggTests(EggTestsBase):
+
+    def test_fsmetadata_from_egg(self):
+        from pkg_resources import require
+        from Products.CMFCore.DirectoryView import addDirectoryViews
+        from Products.CMFCore.DirectoryView import registerDirectory
+
+        faux_globals = {'__name__': 'Products.Rotten'}
+        require('rotten')
+        registerDirectory('skins', faux_globals)
+
+        ob =  DummyFolder()
+        self.failIf('rotten' in ob.__dict__)
+        addDirectoryViews(ob, 'skins', faux_globals)
+        self.failUnless('rotten' in ob.__dict__)
+
+        rotten = ob.rotten
+        template = rotten._getOb('rotten')
+        self.assertEqual(template.title, 'Rotten Template')
+
 def test_suite():
     return TestSuite((
-        makeSuite(FSMetadata),
+        makeSuite(FSMetadataDirectoryTests),
+        makeSuite(FSMetadataEggTests),
         ))
 
 if __name__ == '__main__':



More information about the CMF-checkins mailing list