[Zope3-checkins] SVN: Zope3/branches/zipimport-support/src/zope/filereference/ - extend the interface to support exists/isdir/isfile checks

Fred L. Drake, Jr. fdrake at gmail.com
Wed Nov 9 17:02:23 EST 2005


Log message for revision 40013:
  - extend the interface to support exists/isdir/isfile checks
  - add a specialized zipimport-specific reference implementation that
    can use more direct checks for the checks
  

Changed:
  U   Zope3/branches/zipimport-support/src/zope/filereference/README.txt
  U   Zope3/branches/zipimport-support/src/zope/filereference/__init__.py
  U   Zope3/branches/zipimport-support/src/zope/filereference/interfaces.py
  U   Zope3/branches/zipimport-support/src/zope/filereference/reference.py
  U   Zope3/branches/zipimport-support/src/zope/filereference/tests.py

-=-
Modified: Zope3/branches/zipimport-support/src/zope/filereference/README.txt
===================================================================
--- Zope3/branches/zipimport-support/src/zope/filereference/README.txt	2005-11-09 21:52:05 UTC (rev 40012)
+++ Zope3/branches/zipimport-support/src/zope/filereference/README.txt	2005-11-09 22:02:23 UTC (rev 40013)
@@ -13,9 +13,11 @@
 before this API existed, while new code can use the extended API for
 more flexibility.
 
-There are two interesting functions: `new()` is used to construct a
-new path reference, and `open()` is used to open the resource as a
-file-like object.
+There are several interesting functions: `new()` is used to construct
+a new path reference, and `open()` is used to open the resource as a
+file-like object.  Additional functions correlate to the common
+functions `os.path.exists()`, `os.path.isdir()`, and
+`os.path.isfile()`.
 
 `new()` takes three arguments: a path, a package, and a base path.
 Only the first is required; passing `None` for the `package` and
@@ -47,6 +49,15 @@
   '====================================\n'
   >>> f.close()
 
+We can also ask whether the reference points to a file that exists::
+
+  >>> zope.filereference.exists(ref)
+  True
+  >>> zope.filereference.isfile(ref)
+  True
+  >>> zope.filereference.isdir(ref)
+  False
+
 While this looks little different from using a simple string to refer
 to the referenced file, it provides more functionality if the file
 being referenced is part of a package contained in a ZIP archive.
@@ -70,6 +81,15 @@
   '<configure\n'
   >>> f.close()
 
+The query methods provide the expected results as well::
+
+  >>> zope.filereference.exists(ref)
+  True
+  >>> zope.filereference.isdir(ref)
+  False
+  >>> zope.filereference.isfile(ref)
+  True
+
 Note that only read modes are supported::
 
   >>> zope.filereference.open(ref, "w")
@@ -91,3 +111,25 @@
   Traceback (most recent call last):
     ...
   ValueError: `mode` must be a read-only mode
+
+References to directories in ZIP files work as well::
+
+  >>> ref = zope.filereference.new("sample", package=zippity)
+  >>> zope.filereference.exists(ref)
+  True
+  >>> zope.filereference.isdir(ref)
+  True
+  >>> zope.filereference.isfile(ref)
+  False
+  
+If we refer to a resource in a ZIP archive that doesn't exist, we get
+the expected results::
+
+  >>> ref = zope.filereference.new("MISSING.txt", package=zippity.sample)
+
+  >>> zope.filereference.exists(ref)
+  False
+  >>> zope.filereference.isfile(ref)
+  False
+  >>> zope.filereference.isdir(ref)
+  False

Modified: Zope3/branches/zipimport-support/src/zope/filereference/__init__.py
===================================================================
--- Zope3/branches/zipimport-support/src/zope/filereference/__init__.py	2005-11-09 21:52:05 UTC (rev 40012)
+++ Zope3/branches/zipimport-support/src/zope/filereference/__init__.py	2005-11-09 22:02:23 UTC (rev 40013)
@@ -19,4 +19,4 @@
 __docformat__ = "reStructuredText"
 
 
-from reference import open, new
+from reference import open, new, exists, isdir, isfile

Modified: Zope3/branches/zipimport-support/src/zope/filereference/interfaces.py
===================================================================
--- Zope3/branches/zipimport-support/src/zope/filereference/interfaces.py	2005-11-09 21:52:05 UTC (rev 40012)
+++ Zope3/branches/zipimport-support/src/zope/filereference/interfaces.py	2005-11-09 22:02:23 UTC (rev 40013)
@@ -27,3 +27,12 @@
         Only 'read' modes are supported.
 
         """
+
+    def isfile():
+        """Returns True iff the resource exists and is a file."""
+
+    def isdir():
+        """Returns True iff the resource exists and is a directory."""
+
+    def exists():
+        """Returns True iff the resource exists."""

Modified: Zope3/branches/zipimport-support/src/zope/filereference/reference.py
===================================================================
--- Zope3/branches/zipimport-support/src/zope/filereference/reference.py	2005-11-09 21:52:05 UTC (rev 40012)
+++ Zope3/branches/zipimport-support/src/zope/filereference/reference.py	2005-11-09 22:02:23 UTC (rev 40013)
@@ -20,6 +20,7 @@
 import errno
 import os
 import StringIO
+import zipimport
 
 import zope.interface
 
@@ -31,6 +32,27 @@
     pkg_resources = None
 
 
+def exists(path):
+    if IResourceReference.providedBy(path):
+        return path.exists()
+    else:
+        return os.path.exists(path)
+
+
+def isdir(path):
+    if IResourceReference.providedBy(path):
+        return path.isdir()
+    else:
+        return os.path.isdir(path)
+
+
+def isfile(path):
+    if IResourceReference.providedBy(path):
+        return path.isfile()
+    else:
+        return os.path.isfile(path)
+
+
 def open(path, mode="rb"):
     if not mode.startswith("r"):
         raise ValueError("`mode` must be a read-only mode")
@@ -60,7 +82,11 @@
     p = os.path.abspath(p)
 
     if package:
-        return PackagePathReference(p, package, path)
+        loader = getattr(package, "__loader__", None)
+        if isinstance(loader, zipimport.zipimporter):
+            return ZipImporterPackagePathReference(p, package, path)
+        else:
+            return PackagePathReference(p, package, path)
     else:
         return PathReference(p)
 
@@ -76,11 +102,28 @@
     def open(self, mode="rb"):
         return __builtin__.open(self, mode)
 
+    def isfile(self):
+        return os.path.isfile(self)
 
-class PackagePathReference(str):
+    def isdir(self):
+        return os.path.isdir(self)
 
+    def exists(self):
+        return os.path.exists(self)
+
+
+class PackageStr(str):
+
     zope.interface.implements(IResourceReference)
 
+    def __add__(self, other):
+        value = str(self) + other
+        relpath = self._relpath + other
+        return self.__class__(value, self._package, relpath)
+
+
+class PackagePathReference(PackageStr):
+
     def __new__(cls, value, package, relpath):
         assert package
         self = str.__new__(cls, value)
@@ -88,13 +131,9 @@
         self._relpath = relpath
         return self
 
-    def __add__(self, other):
-        value = str(self) + other
-        relpath = self._relpath + other
-        return self.__class__(value, self._package, relpath)
-
     def open_pkg_resources(self, mode="rb"):
-        return self._open_packaged_resource(
+        return _open_packaged_resource(
+            self,
             mode,
             pkg_resources.resource_string,
             self._package.__name__,
@@ -112,8 +151,8 @@
         else:
             dir = os.path.dirname(self._package.__file__)
             filename = os.path.join(dir, self._relpath)
-            return self._open_packaged_resource(
-                mode, loader.get_data, filename)
+            return _open_packaged_resource(
+                self, mode, loader.get_data, filename)
 
     def open(self, mode="rb"):
         #
@@ -127,15 +166,123 @@
         else:
             return self.open_path_or_loader(mode)
 
-    def _open_packaged_resource(self, mode, opener, *args):
+    def isfile(self):
         try:
-            data = opener(*args)
-        except IOError, e:
-            if len(e.args) == 1:
-                raise IOError(errno.ENOENT, "file not found", self)
+            f = self.open()
+        except IOError:
+            return False
+        f.close()
+        return True
+
+    def isdir(self):
+        if pkg_resources:
+            return pkg_resources.resource_isdir(
+                self._package.__name__, self._relpath)
+        else:
+            try:
+                loader = self._package.__loader__
+            except AttributeError:
+                for dir in self._package.__path__:
+                    filename = os.path.join(dir, self._relpath)
+                    if os.path.isdir(filename):
+                        return True
+                return False
             else:
-                raise
-        f = StringIO.StringIO(data)
-        f.name = self
-        f.mode = mode
-        return f
+                dir = os.path.dirname(self._package.__file__)
+                path = os.path.join(dir, self._relpath)
+                try:
+                    if loader.is_package(path):
+                        return True
+                except zipimport.ZipImportError:
+                    pass
+                try:
+                    loader.get_data(path)
+                except IOError:
+                    pass
+                else:
+                    return False
+
+    def exists(self):
+        if pkg_resources:
+            return pkg_resources.resource_exists(
+                self._package.__name__, self._relpath)
+        else:
+            try:
+                loader = self._package.__loader__
+            except AttributeError:
+                for dir in self._package.__path__:
+                    filename = os.path.join(dir, self._relpath)
+                    if os.path.exists(filename):
+                        return True
+                return False
+            else:
+                dir = os.path.dirname(self._package.__file__)
+                path = os.path.join(dir, self._relpath)
+                try:
+                    if loader.is_package(path):
+                        return True
+                except zipimport.ZipImportError:
+                    pass
+                try:
+                    loader.get_data(path)
+                except IOError:
+                    pass
+                else:
+                    return True
+
+
+class ZipImporterPackagePathReference(PackageStr):
+
+    def __new__(cls, value, package, relpath):
+        assert package
+        assert isinstance(package.__loader__, zipimport.zipimporter)
+        self = str.__new__(cls, value)
+        self._package = package
+        self._relpath = relpath
+        self._loader = package.__loader__
+        return self
+
+    def open(self, mode="rb"):
+        dir = os.path.dirname(self._package.__file__)
+        filename = os.path.join(dir, self._relpath)
+        return _open_packaged_resource(
+            self, mode, self._loader.get_data, filename)
+
+    def exists(self):
+        relpath = self._getpath()
+        if relpath in self._loader._files:
+            return True
+        relpath += os.path.sep
+        return relpath in self._loader._files
+
+    def isdir(self):
+        relpath = self._getpath()
+        return (relpath + os.sep) in self._loader._files
+
+    def isfile(self):
+        relpath = self._getpath()
+        return relpath in self._loader._files
+
+    def _getpath(self):
+        relpath = self._relpath.replace("/", os.sep)
+        if os.altsep:
+            relpath = relpath.replace(os.altsep, os.sep)
+        while relpath.endswith(os.sep):
+            relpath = relpath[:-len(os.sep)]
+        pkglastname = self._package.__name__.split(".")[-1]
+        relpath = os.path.join(self._loader.prefix, pkglastname, relpath)
+        return relpath
+
+
+def _open_packaged_resource(self, mode, opener, *args):
+    try:
+        data = opener(*args)
+    except IOError, e:
+        if len(e.args) == 1:
+            raise IOError(errno.ENOENT, "file not found", self)
+        else:
+            raise
+    f = StringIO.StringIO(data)
+    f.name = self
+    f.mode = mode
+    return f

Modified: Zope3/branches/zipimport-support/src/zope/filereference/tests.py
===================================================================
--- Zope3/branches/zipimport-support/src/zope/filereference/tests.py	2005-11-09 21:52:05 UTC (rev 40012)
+++ Zope3/branches/zipimport-support/src/zope/filereference/tests.py	2005-11-09 22:02:23 UTC (rev 40013)
@@ -52,11 +52,14 @@
 
 def test_suite():
     suite = unittest.TestSuite()
-    suite.addTest(doctest.DocFileSuite(
+    suite.addTest(doctest.DocFileTest(
         "README.txt",
         setUp=sys_path_setUp, tearDown=sys_path_tearDown))
     if pkg_resources is not None:
-        suite.addTest(doctest.DocFileSuite(
+        name_prefix = zope.filereference.__name__ + "/README.txt - "
+        test = doctest.DocFileTest(
             "README.txt",
-            setUp=pkg_resources_setUp, tearDown=pkg_resources_tearDown))
+            setUp=pkg_resources_setUp, tearDown=pkg_resources_tearDown)
+        test._dt_test.name = name_prefix + "without pkg_resources"
+        suite.addTest(test)
     return suite



More information about the Zope3-Checkins mailing list