javaclass

Changeset

78:f08b72e9f060
2004-12-08 Paul Boddie raw files shortlog changelog graph Added .jar archive support to the import hook. Fixed accidental attempts to import classes as modules.
classhook.py (file)
     1.1 --- a/classhook.py	Wed Dec 08 22:49:44 2004 +0100
     1.2 +++ b/classhook.py	Wed Dec 08 23:02:46 2004 +0100
     1.3 @@ -1,13 +1,16 @@
     1.4  #!/usr/bin/env python
     1.5  
     1.6 -import ihooks
     1.7 -import os, glob
     1.8 -from imp import PY_SOURCE, PKG_DIRECTORY, C_BUILTIN
     1.9 -import classfile, bytecode
    1.10 -import new
    1.11 +import ihooks # for the import machinery
    1.12 +import os, glob # for getting suitably-named files
    1.13 +from imp import PY_SOURCE, PKG_DIRECTORY, C_BUILTIN # import machinery magic
    1.14 +import classfile, bytecode # Java class support
    1.15 +import zipfile # for Java archive inspection
    1.16 +
    1.17 +# NOTE: Arbitrary constants pulled from thin air.
    1.18  
    1.19  JAVA_PACKAGE = 20041113
    1.20  JAVA_CLASS = 20041114
    1.21 +JAVA_ARCHIVE = 20041115
    1.22  
    1.23  class ClassHooks(ihooks.Hooks):
    1.24  
    1.25 @@ -17,7 +20,86 @@
    1.26  
    1.27          "Return the recognised suffixes."
    1.28  
    1.29 -        return ihooks.Hooks.get_suffixes(self) + [("", "", JAVA_PACKAGE), (os.extsep + "class", "r", JAVA_CLASS)]
    1.30 +        return [("", "", JAVA_PACKAGE), (os.extsep + "jar", "r", JAVA_ARCHIVE)] + ihooks.Hooks.get_suffixes(self)
    1.31 +
    1.32 +    def path_isdir(self, x, archive=None):
    1.33 +
    1.34 +        "Return whether 'x' is a directory in the given 'archive'."
    1.35 +
    1.36 +        if archive is None:
    1.37 +            return ihooks.Hooks.path_isdir(self, x)
    1.38 +
    1.39 +        return self._get_dirname(x) in archive.namelist()
    1.40 +
    1.41 +    def _get_dirname(self, x):
    1.42 +
    1.43 +        """
    1.44 +        Return the directory name for 'x'.
    1.45 +        In zip files, the presence of "/" seems to indicate a directory.
    1.46 +        """
    1.47 +
    1.48 +        if x.endswith("/"):
    1.49 +            return x
    1.50 +        else:
    1.51 +            return x + "/"
    1.52 +
    1.53 +    def listdir(self, x, archive=None):
    1.54 +
    1.55 +        "Return the contents of the directory 'x' in the given 'archive'."
    1.56 +
    1.57 +        if archive is None:
    1.58 +            return ihooks.Hooks.listdir(self, x)
    1.59 +
    1.60 +        x = self._get_dirname(x)
    1.61 +        l = []
    1.62 +        for path in archive.namelist():
    1.63 +
    1.64 +            # Find out if the path is within the given directory.
    1.65 +
    1.66 +            if path != x and path.startswith(x):
    1.67 +
    1.68 +                # Get the path below the given directory.
    1.69 +
    1.70 +                subpath = path[len(x):]
    1.71 +
    1.72 +                # Find out whether the path is an object in the current directory.
    1.73 +
    1.74 +                if subpath.count("/") == 0 or subpath.count("/") == 1 and subpath.endswith("/"):
    1.75 +                    l.append(subpath)
    1.76 +
    1.77 +        return l
    1.78 +
    1.79 +    def matching(self, dir, extension, archive=None):
    1.80 +
    1.81 +        """
    1.82 +        Return the matching files in the given directory 'dir' having the given
    1.83 +        'extension' within the given 'archive'. Produce a list containing full
    1.84 +        paths as opposed to simple filenames.
    1.85 +        """
    1.86 +
    1.87 +        if archive is None:
    1.88 +            return glob.glob(self.path_join(dir, "*" + extension))
    1.89 +
    1.90 +        dir = self._get_dirname(dir)
    1.91 +        l = []
    1.92 +        for path in self.listdir(dir, archive):
    1.93 +            if path.endswith(extension):
    1.94 +                l.append(self.path_join(dir, path))
    1.95 +        return l
    1.96 +
    1.97 +    def read(self, filename, archive=None):
    1.98 +
    1.99 +        """
   1.100 +        Return the contents of the file with the given 'filename' in the given
   1.101 +        'archive'.
   1.102 +        """
   1.103 +
   1.104 +        if archive is None:
   1.105 +            f = open(filename, "rb")
   1.106 +            s = f.read()
   1.107 +            f.close()
   1.108 +            return s
   1.109 +        return archive.read(filename)
   1.110  
   1.111  class ClassLoader(ihooks.ModuleLoader):
   1.112  
   1.113 @@ -36,39 +118,80 @@
   1.114          if result is not None:
   1.115              return result
   1.116  
   1.117 +        # An archive may be opened.
   1.118 +
   1.119 +        archive = None
   1.120 +
   1.121          # Provide a special name for the current directory.
   1.122  
   1.123          if name == "__this__":
   1.124              path = "."
   1.125 +
   1.126 +        # Where no directory is given, return failure immediately.
   1.127 +
   1.128          elif dir is None:
   1.129              return None
   1.130 +
   1.131 +        # Detect archives.
   1.132 +
   1.133          else:
   1.134 -            path = os.path.join(dir, name)
   1.135 +            archive, archive_path, path = self._get_archive_and_path(dir, name)
   1.136  
   1.137 -        #print "Processing name", name, "in", dir, "producing", path
   1.138 +        print "Processing name", name, "in", dir, "producing", path, "within archive", archive
   1.139  
   1.140 -        if self._find_module_at_path(path):
   1.141 -            return (None, path, ("", "", JAVA_PACKAGE))
   1.142 +        if self._find_module_at_path(path, archive):
   1.143 +            if archive is not None:
   1.144 +                return (archive, archive_path + ":" + path, (os.extsep + "jar", "r", JAVA_ARCHIVE))
   1.145 +            else:
   1.146 +                return (None, path, ("", "", JAVA_PACKAGE))
   1.147          else:
   1.148              return None
   1.149  
   1.150 -    def _find_module_at_path(self, path):
   1.151 -        if os.path.isdir(path):
   1.152 +    def _get_archive_and_path(self, dir, name):
   1.153 +        parts = dir.split(":")
   1.154 +        archive_path = parts[0]
   1.155 +
   1.156 +        # Archives may include an internal path, but will in any case have
   1.157 +        # a primary part ending in .jar.
   1.158 +
   1.159 +        if archive_path.endswith(os.extsep + "jar"):
   1.160 +            archive = zipfile.ZipFile(archive_path, "r")
   1.161 +            path = self.hooks.path_join(":".join(parts[1:]), name)
   1.162 +
   1.163 +        # Otherwise, produce a filesystem-based path.
   1.164 +
   1.165 +        else:
   1.166 +            archive = None
   1.167 +            path = self.hooks.path_join(dir, name)
   1.168 +
   1.169 +        return archive, archive_path, path
   1.170 +
   1.171 +    def _get_path_in_archive(self, path):
   1.172 +        parts = path.split(":")
   1.173 +        if len(parts) == 1:
   1.174 +            return parts[0]
   1.175 +        else:
   1.176 +            return ":".join(parts[1:])
   1.177 +
   1.178 +    def _find_module_at_path(self, path, archive):
   1.179 +        if self.hooks.path_isdir(path, archive):
   1.180 +            print "Looking in", path, "using archive", archive
   1.181  
   1.182              # Look for classes in the directory.
   1.183  
   1.184 -            if len(glob.glob(os.path.join(path, "*" + os.extsep + "class"))) != 0:
   1.185 +            if len(self.hooks.matching(path, os.extsep + "class", archive)) != 0:
   1.186                  return 1
   1.187  
   1.188              # Otherwise permit importing where directories containing classes exist.
   1.189  
   1.190 -            for filename in os.listdir(path):
   1.191 -                pathname = os.path.join(path, filename)
   1.192 -                result = self._find_module_at_path(pathname)
   1.193 +            print "Filenames are", self.hooks.listdir(path, archive)
   1.194 +            for filename in self.hooks.listdir(path, archive):
   1.195 +                pathname = self.hooks.path_join(path, filename)
   1.196 +                result = self._find_module_at_path(pathname, archive)
   1.197                  if result is not None:
   1.198                      return result
   1.199  
   1.200 -        return None
   1.201 +        return 0
   1.202  
   1.203      def load_module(self, name, stuff):
   1.204  
   1.205 @@ -81,12 +204,12 @@
   1.206  
   1.207          # Just go into the directory and find the class files.
   1.208  
   1.209 -        file, filename, info = stuff
   1.210 +        archive, filename, info = stuff
   1.211          suffix, mode, datatype = info
   1.212 -        if datatype != JAVA_PACKAGE:
   1.213 +        if datatype not in (JAVA_PACKAGE, JAVA_ARCHIVE):
   1.214              return ihooks.ModuleLoader.load_module(self, name, stuff)
   1.215  
   1.216 -        print "Loading", file, filename, info
   1.217 +        print "Loading", archive, filename, info
   1.218  
   1.219          # Set up the module.
   1.220  
   1.221 @@ -103,14 +226,17 @@
   1.222          class_files = []
   1.223          classes = []
   1.224  
   1.225 +        # Get the real filename.
   1.226 +
   1.227 +        filename = self._get_path_in_archive(filename)
   1.228 +        print "Real filename", filename
   1.229 +
   1.230          # Load the class files.
   1.231  
   1.232          class_files = {}
   1.233 -        for class_filename in glob.glob(os.path.join(filename, "*" + os.extsep + "class")):
   1.234 +        for class_filename in self.hooks.matching(filename, os.extsep + "class", archive):
   1.235              print "Loading class", class_filename
   1.236 -            f = open(class_filename, "rb")
   1.237 -            s = f.read()
   1.238 -            f.close()
   1.239 +            s = self.hooks.read(class_filename, archive)
   1.240              class_file = classfile.ClassFile(s)
   1.241              class_files[str(class_file.this_class.get_name())] = class_file
   1.242  
   1.243 @@ -174,7 +300,6 @@
   1.244  
   1.245          return module
   1.246  
   1.247 -importer = ihooks.ModuleImporter(loader=ClassLoader(hooks=ClassHooks()))
   1.248 -importer.install()
   1.249 +ihooks.ModuleImporter(loader=ClassLoader(hooks=ClassHooks())).install()
   1.250  
   1.251  # vim: tabstop=4 expandtab shiftwidth=4