Ronald Oussoren avatar Ronald Oussoren committed e16188e

Use modulegraph.zipio instead of hardcoding zipfile access

Modulegraph now has a module for dealing with zipfile access, use that
instead of duplicating the functionality here. The old interface is
temporarily still present, but will be removed soon.

Comments (0)

Files changed (5)

doc/changelog.rst

 Release history
 ===============
 
-py2app 0.5.3
-------------
+py2app 0.6
+----------
 
-py2app 0.5.3 is a minor feature release
+py2app 0.6 is a minor feature release
 
 
 Features:
   use this, the argv emulation code will ensure that the URL
   to open will end up in ``sys.argv``.
 
+- ``py2app.util`` contains a number of functions that are now
+  deprecated an will be removed in a future version, specifically:
+  ``os_path_islink``, ``os_path_isdir``, ``path_to_zip``,
+  ``get_zip_data``, ``get_mtime``,  and ``os_readlink``.
+
+- The module ``py2app.simpleio`` no longer exists, and should never
+  have been in the repository (it was part of a failed rewrite of
+  the I/O layer).
+
 Bug fixes:
 
 - The ``--alias`` option didn't work when building a plugin

py2app/build_app.py

 from altgraph.compat import *
 
 from modulegraph.find_modules import find_modules, parse_mf_results
-from modulegraph.modulegraph import SourceModule, Package, os_listdir
+from modulegraph.modulegraph import SourceModule, Package
+from modulegraph import zipio
 
 import macholib.dyld
 import macholib.MachOStandalone
     fancy_split, byte_compile, make_loader, imp_find_module, \
     copy_tree, fsencoding, strip_files, in_system_path, makedirs, \
     iter_platform_files, find_version, skipscm, momc, copy_file, \
-    os_path_isdir, copy_resource
+    copy_resource
 from py2app.filters import \
     not_stdlib_filter, not_system_filter, has_filename_filter
 from py2app import recipes
 
         target_dir = os.path.join(target_dir, *(package.identifier.split('.')))
         for dname in package.packagepath:
-            filenames = filter(datafilter, os_listdir(dname))
+            filenames = filter(datafilter, zipio.listdir(dname))
             for fname in filenames:
                 if fname in ('.svn', 'CVS'):
                     # Scrub revision manager junk
                 pth = os.path.join(dname, fname)
 
                 # Check if we have found a package, exclude those
-                if os_path_isdir(pth):
-                    for p in os_listdir(pth):
+                if zipio.isdir(pth):
+                    for p in zipio.listdir(pth):
                         if p.startswith('__init__.') and p[8:] in exts:
                             break
 

py2app/simpleio.py

-"""
-A simple file-system like interface that supports
-both the regular filesystem and zipfiles
-"""
-__all__ = ('FileIO', 'ReadOnlyIO')
-
-import os, time, zipfile
-
-class FileIO (object):
-    """
-    A simple interface that makes it possible
-    to write simple filesystem structures using
-    the interface that's exposed by the zipfile
-    module.
-    """
-    def __init__(self, prefix):
-        self.prefix = prefix
-
-    def writestr(self, path, data):
-        """
-        Write 'data' into file at 'path',
-        using read-only file permissions.
-        """
-        while path.startswith('/'):
-            path = path[1:]
-        fname = os.join(self.prefix, path)
-        dirname = os.path.dirname(fname)
-        if not os.path.exists(fname):
-            os.makedirs(fname, mode=0755)
-        fp = open(fname, 'wb')
-        fp.write(data)
-        fp.close()
-        os.chmod(fname, 0444)
-
-class ReadOnlyIO (object):
-    """
-    A minimal read-only interface to the filesystem.
-
-    This interface transparently deals with zipfiles
-    (that is,   ``io.read('/foo.zip/bar')`` extracts
-    the contents of ``bar`` from the zipfile.
-
-    This interface is designed to be useful for py2app
-    and is not intended to be fast or generally useful.
-    """
-
-
-    def read(self, path):
-        """
-        Return the contents of ``path``
-        """
-        zf, zp = self._zippath(path)
-
-        if zf is None:
-            fp = open(path, 'rb')
-            data = fp.read()
-            fp.close()
-            
-            return data
-
-        else:
-            zf = zipfile.ZipFile(zf, 'r')
-            return zf.read(zp)
-
-    def get_mtime(self, path):
-        """
-        Return the ``mtime`` attribute of ``path``.
-        """
-        zf, zp = self._zippath(path)
-
-        if zf is None:
-            return os.stat(path).st_mtime
-
-        else:
-            zf = zipfile.ZipFile(zf)
-            info = zf.getinfo(zp)
-            return time.mktime(info.date_time + (0, 0, 0))
-
-
-    def exists(self, path):
-        """
-        Return True if ``path`` exists
-        """
-        return self.is_file(path) or self.is_dir(path) or self.is_symlink(path)
-
-    def is_dir(self, path):
-        """
-        Return True if ``path`` exists and is a directory
-        """
-        zf, zp = self._zippath(path, strict=False)
-        if zf is None:
-            return os.path.isdir(path)
-
-        return bool(listdir(path))
-
-    def is_symlink(self, path):
-        """
-        Return True if ``path`` exists and is a symbolic link
-        """
-        zf, zp = self._zippath(path, strict=False)
-        if zf is not None:
-            return False
-
-        return os.path.islink(path)
-
-    def readlink(self, path):
-        zf, zp = self._zippath(path)
-        if zf is None:
-            return os.readlink(path)
-
-        raise IOError("%r is not a symlink"%(path,))
-
-    def is_file(self, path):
-        """
-        Return True if ``path`` exists and is a regular file
-        """
-        try:
-            zf, zp = self._zippath(self, path, strict=True)
-        
-        except IOError:
-            return False
-
-        if zf is None:
-            return os.path.isdir(path)
-
-        else:
-            # 'strict==True' hence the object must
-            # exist in the zipfile and should therefore
-            # be a file and not a directory or link.
-            return True
-
-    def listdir(self, path):
-        """
-        Return the contents of directory at ``path``.
-
-        NOTE: if ``path`` is in a zipfile this will
-        not raise an error if the directory does not
-        exist.
-        """
-        zf, zp = self._zippath(path, strict=False)
-
-        if zf is None:
-            return os.listdir(path)
-
-        else:
-            _zf = zf
-            zf = zipfile.ZipFile(zf, 'r')
-            rest = rest + '/'
-
-            result = set()
-            for nm in zf.namelist():
-                if nm == rest:
-                    raise IOError("%r is not a directory in %r"%(path, _zf))
-
-                if nm.startswith(rest):
-                    result.add(nm[len(rest):].split('/')[0])
-
-            return list(result)
-
-    def _zippath(self, path, strict=True):
-        """
-        Return either ``(zipfilename, zippath)``  or ``(None, path)``
-
-        If ``zipfilename`` is not None is points to a zipfile
-        that may contain the file as ``zippath``. Otherwise
-        the file is definitely not in a zipfile
-
-        Raises ``IOError`` when the file doesn't exist, but won't
-        check if the file exists in the zipfile unless ``strict``
-        is True.
-        """
-        if os.path.exists(path):
-            return (None, path)
-
-        else:
-            rest = ''
-            while curpath and not os.path.exists(curpath):
-                curpath, r = os.path.split(curpath)
-                rest = os.path.join(r, rest)
-
-            if not curpath:
-                raise IOError("file %r does not exist"%(path,))
-
-            try:
-                zf = zipfile.ZipFile(curpath)
-            except zipfile.BadZipfile:
-                raise IOError("bad zipfile %r for %r"%(curpath, path))
-
-            if rest.endswith('/'):
-                rest = rest[:-1]
-
-            if strict:
-                try:
-                    zf.getinfo(rest)
-                except KeyError:
-                    raise IOError("file %r does not exist in %r", path, curpath)
-
-            return curpath, rest
 from pkg_resources import require
 require("modulegraph", "altgraph", "macholib")
 
-import os, sys, imp, zipfile, time
+import os, sys, imp, time
 from modulegraph.find_modules import PY_SUFFIXES, C_SUFFIXES
 from modulegraph.util import *
-from modulegraph.modulegraph import os_listdir
+from modulegraph import zipio
 from altgraph.compat import *
 import macholib.util
+import warnings
 
 import pkg_resources
 
+# Deprecated functionality
 def os_path_islink(path):
-    """
-    os.path.islink with zipfile support.
+    warnings.warn("Use zipio.islink instead of os_path_islink",
+            DeprecationWarning)
+    return zipio.islink(path)
 
-    Luckily zipfiles cannot contain symlink, therefore the implementation is
-    trivial.
-    """
-    return os.path.islink(path)
+def os_path_isdir(path):
+    warnings.warn("Use zipio.isdir instead of os_path_isdir",
+            DeprecationWarning)
+    return zipio.islink(path)
 
 def os_readlink(path):
+    warnings.warn("Use zipio.readlink instead of os_readlink",
+            DeprecationWarning)
+    return zipio.islink(path)
+
+import zipfile
+def get_zip_data(path_to_zip, path_in_zip):
+    warnings.warn("Use zipio.open instead of get_zip_data",
+            DeprecationWarning)
+    zf = zipfile.ZipFile(path_to_zip)
+    return zf.read(path_in_zip)
+
+def path_to_zip(path):
     """
-    os.readlink with zipfile support.
-
-    Luckily zipfiles cannot contain symlink, therefore the implementation is
-    trivial.
+    Returns (pathtozip, pathinzip). If path isn't in a zipfile pathtozip
+    will be None
     """
-    return os.readlink(path)
-
-def os_path_isdir(path):
-    """
-    os.path.isdir that understands zipfiles.
-
-    Assumes that you're checking a path the is the result of os_listdir and 
-    might give false positives otherwise.
-    """
-    while path.endswith('/') and path != '/':
-        path = path[:-1]
-
-    zf, zp = path_to_zip(path)
-    if zf is None:
-        return os.path.isdir(zp)
+    warnings.warn("Don't use this function", DeprecationWarning)
+    zf = zipfile.ZipFile(path_to_zip)
+    orig_path = path
+    from distutils.errors import DistutilsFileError
+    if os.path.exists(path):
+        return (None, path)
 
     else:
-        zip = zipfile.ZipFile(zf)
+        rest = ''
+        while not os.path.exists(path):
+            path, r = os.path.split(path)
+            if not path:
+                raise DistutilsFileError("File doesn't exist: %s"%(orig_path,))
+            rest = os.path.join(r, rest)
+
+        if not os.path.isfile(path):
+            # Directory really doesn't exist
+            raise DistutilsFileError("File doesn't exist: %s"%(orig_path,))
+
         try:
-            info = zip.getinfo(zp)
+           zf = zipfile.ZipFile(path)
+        except zipfile.BadZipfile:
+            raise DistutilsFileError("File doesn't exist: %s"%(orig_path,))
 
-        except KeyError:
-            return True
+        if rest.endswith('/'):
+            rest = rest[:-1]
 
-        else:
-            # Not quite true, you can store information about directories in
-            # zipfiles, but those have a lash at the end of the filename
-            return False
+        return path, rest
+
+def get_mtime(path, mustExist=True):
+    """
+    Get mtime of a path, even if it is inside a zipfile
+    """
+    warnings.warn("Don't use this function", DeprecationWarning)
+    try:
+        return zipio.getmtime(target)
+
+    except IOError:
+        if not mustExist:
+            return -1
+
+        raise
+
+# End deprecated functionality
 
 gConverterTab = {}
 def find_converter(source):
         if not dry_run:
             if not os.path.exists(destination):
                 os.mkdir(destination)
-        for fn in os_listdir(source):
+        for fn in zipio.listdir(source):
             copy_resource(os.path.join(source, fn), 
                     os.path.join(destination, fn), dry_run=dry_run)
 
 
 
 def copy_file(source, destination, dry_run=0):
-    zf, zp = path_to_zip(source)
-    if zf is None:
-        data = open(zp,'rb').read()
-    else:
-        data = get_zip_data(zf, zp)
+    fp_in = zipio.open(source, 'rb')
+    fp_out = None
+    try:
+        if not dry_run:
+            fp_out = open(destination, 'wb')
+            fp_out.write(data)
 
-    if not dry_run:
-        fp = open(destination, 'wb')
-        fp.write(data)
-        fp.close()
+    finally:
+        fp_in.close()
+        if fp_out is not None:
+            fp_out.close()
 
-def get_zip_data(path_to_zip, path_in_zip):
-    zf = zipfile.ZipFile(path_to_zip)
-    return zf.read(path_in_zip)
 
-def path_to_zip(path):
-    """
-    Returns (pathtozip, pathinzip). If path isn't in a zipfile pathtozip
-    will be None
-    """
-    orig_path = path
-    from distutils.errors import DistutilsFileError
-    if os.path.exists(path):
-        return (None, path)
 
-    else:
-        rest = ''
-        while not os.path.exists(path):
-            path, r = os.path.split(path)
-            if not path:
-                raise DistutilsFileError("File doesn't exist: %s"%(orig_path,))
-            rest = os.path.join(r, rest)
 
-        if not os.path.isfile(path):
-            # Directory really doesn't exist
-            raise DistutilsFileError("File doesn't exist: %s"%(orig_path,))
-
-        try:
-           zf = zipfile.ZipFile(path)
-        except zipfile.BadZipfile:
-            raise DistutilsFileError("File doesn't exist: %s"%(orig_path,))
-
-        if rest.endswith('/'):
-            rest = rest[:-1]
-
-        return path, rest
-
-
-def get_mtime(path, mustExist=True):
-    """
-    Get mtime of a path, even if it is inside a zipfile
-    """
-
-    try:
-        return os.stat(path).st_mtime
-
-    except os.error:
-        from distutils.errors import DistutilsFileError
-        try:
-            path, rest = path_to_zip(path)
-        except DistutilsFileError:
-            if not mustExist:
-                return -1
-            raise
-
-        zf = zipfile.ZipFile(path)
-        info = zf.getinfo(rest)
-        return time.mktime(info.date_time + (0, 0, 0))
 
 def newer(source, target):
     """
     distutils.dep_utils.newer with zipfile support
     """
-
-    msource = get_mtime(source)
-    mtarget = get_mtime(target, mustExist=False)
-
-    return msource > mtarget
-
+    try:
+        return zipio.getmtime(source) > zipio.getmtime(target)
+    except IOError:
+        return True
 
 
 def find_version(fn):
                     suffix = os.path.splitext(mod.filename)[1]
 
                     if suffix in ('.py', '.pyw'):
-                        zfile, pth = path_to_zip(mod.filename)
-                        if zfile is None:
-                            compile(mod.filename, cfile, dfile)
-                        else:
-                            fn = dfile + '.py'
-                            open(fn, 'wb').write(get_zip_data(zfile, pth))
-                            compile(mod.filename, cfile, dfile)
-                            os.unlink(fn)
+                        fn = dfile + '.py'
+
+                        fp_in = zipio.open(mod.filename, 'rb')
+                        fp_out = open(fn, 'wb')
+                        fp_out.write(fp_in.read())
+                        fp_in.close()
+                        fp_out.close()
+
+                        compile(fn, cfile, dfile)
+                        os.unlink(fn)
 
                     elif suffix in PY_SUFFIXES:
                         # Minor problem: This will happily copy a file
     if condition is None:
         condition = skipscm
 
-    if not dry_run and not os_path_isdir(src):
+    if not dry_run and not os.path.isdir(src):
         raise DistutilsFileError, \
               "cannot copy tree '%s': not a directory" % src
     try:
-        names = os_listdir(src)
+        names = zipio.listdir(src)
     except os.error, (errno, errstr):
         if dry_run:
             names = []
         if (condition is not None) and (not condition(src_name)):
             continue
 
-        if preserve_symlinks and os_path_islink(src_name):
-            link_dest = os_readlink(src_name)
+        if preserve_symlinks and zipio.islink(src_name):
+            link_dest = zipio.readlink(src_name)
             log.info("linking %s -> %s", dst_name, link_dest)
             if not dry_run:
                 if update and not newer(src, dst_name):
                     pass
                 else:
-                    if os_path_islink(dst_name):
+                    if zipio.islink(dst_name):
                         os.remove(dst_name)
                     os.symlink(link_dest, dst_name)
             outputs.append(dst_name)
 
-        elif os_path_isdir(src_name):
+        elif os.path.isdir(src_name):
             outputs.extend(
                 copy_tree(src_name, dst_name, preserve_mode,
                           preserve_times, preserve_symlinks, update,
 setup(
     # metadata
     name='py2app',
-    version='0.5.3',
+    version='0.6',
     description='Create standalone Mac OS X applications with Python',
     author='Bob Ippolito',
     author_email='bob@redivi.com',
     long_description=LONG_DESCRIPTION,
     classifiers=CLASSIFIERS,
     install_requires=[
-        "altgraph>=0.7",
-        "modulegraph>=0.8.1",
-        "macholib>=1.3",
+        "altgraph>=0.9",
+        "modulegraph>=0.9",
+        "macholib>=1.4",
     ],
     tests_require=tests_require,
     cmdclass=dict(
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.