Commits

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.

  • Participants
  • Parent commits 8bda993

Comments (0)

Files changed (5)

File 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

File 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
 

File 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

File py2app/util.py

 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(