[Python-checkins] CVS: python/dist/src/Demo/imputil importers.py,NONE,1.1

Greg Stein python-dev@python.org
Thu, 29 Jun 2000 04:05:34 -0700


Update of /cvsroot/python/python/dist/src/Demo/imputil
In directory slayer.i.sourceforge.net:/tmp/cvs-serv11808

Added Files:
	importers.py 
Log Message:
demonstration importers



--- NEW FILE ---
#
# importers.py
#
# Demonstration subclasses of imputil.Importer
#

# There should be consideration for the imports below if it is desirable
# to have "all" modules be imported through the imputil system.

# these are C extensions
import sys
import imp
import struct
import marshal

# these are .py modules
import imputil
import os

######################################################################

_TupleType = type(())
_StringType = type('')

######################################################################

# byte-compiled file suffic character
_suffix_char = __debug__ and 'c' or 'o'

# byte-compiled file suffix
_suffix = '.py' + _suffix_char

# the C_EXTENSION suffixes
_c_suffixes = filter(lambda x: x[2] == imp.C_EXTENSION, imp.get_suffixes())

def _timestamp(pathname):
  "Return the file modification time as a Long."
  try:
    s = os.stat(pathname)
  except OSError:
    return None
  return long(s[8])

def _fs_import(dir, modname, fqname):
  "Fetch a module from the filesystem."

  pathname = os.path.join(dir, modname)
  if os.path.isdir(pathname):
    values = { '__pkgdir__' : pathname, '__path__' : [ pathname ] }
    ispkg = 1
    pathname = os.path.join(pathname, '__init__')
  else:
    values = { }
    ispkg = 0

    # look for dynload modules
    for desc in _c_suffixes:
      file = pathname + desc[0]
      try:
        fp = open(file, desc[1])
      except IOError:
        pass
      else:
        module = imp.load_module(fqname, fp, file, desc)
        values['__file__'] = file
        return 0, module, values

  t_py = _timestamp(pathname + '.py')
  t_pyc = _timestamp(pathname + _suffix)
  if t_py is None and t_pyc is None:
    return None
  code = None
  if t_py is None or (t_pyc is not None and t_pyc >= t_py):
    file = pathname + _suffix
    f = open(file, 'rb')
    if f.read(4) == imp.get_magic():
      t = struct.unpack('<I', f.read(4))[0]
      if t == t_py:
        code = marshal.load(f)
    f.close()
  if code is None:
    file = pathname + '.py'
    code = _compile(file, t_py)

  values['__file__'] = file
  return ispkg, code, values

######################################################################
#
# Simple function-based importer
#
class FuncImporter(imputil.Importer):
  "Importer subclass to use a supplied function rather than method overrides."
  def __init__(self, func):
    self.func = func
  def get_code(self, parent, modname, fqname):
    return self.func(parent, modname, fqname)

def install_with(func):
  FuncImporter(func).install()


######################################################################
#
# Base class for archive-based importing
#
class PackageArchiveImporter(imputil.Importer):
  """Importer subclass to import from (file) archives.

  This Importer handles imports of the style <archive>.<subfile>, where
  <archive> can be located using a subclass-specific mechanism and the
  <subfile> is found in the archive using a subclass-specific mechanism.

  This class defines two hooks for subclasses: one to locate an archive
  (and possibly return some context for future subfile lookups), and one
  to locate subfiles.
  """

  def get_code(self, parent, modname, fqname):
    if parent:
      # the Importer._finish_import logic ensures that we handle imports
      # under the top level module (package / archive).
      assert parent.__importer__ == self

      # if a parent "package" is provided, then we are importing a sub-file
      # from the archive.
      result = self.get_subfile(parent.__archive__, modname)
      if result is None:
        return None
      if isinstance(result, _TupleType):
        assert len(result) == 2
        return (0,) + result
      return 0, result, {}

    # no parent was provided, so the archive should exist somewhere on the
    # default "path".
    archive = self.get_archive(modname)
    if archive is None:
      return None
    return 1, "", {'__archive__':archive}

  def get_archive(self, modname):
    """Get an archive of modules.

    This method should locate an archive and return a value which can be
    used by get_subfile to load modules from it. The value may be a simple
    pathname, an open file, or a complex object that caches information
    for future imports.

    Return None if the archive was not found.
    """
    raise RuntimeError, "get_archive not implemented"

  def get_subfile(self, archive, modname):
    """Get code from a subfile in the specified archive.

    Given the specified archive (as returned by get_archive()), locate
    and return a code object for the specified module name.

    A 2-tuple may be returned, consisting of a code object and a dict
    of name/values to place into the target module.

    Return None if the subfile was not found.
    """
    raise RuntimeError, "get_subfile not implemented"


class PackageArchive(PackageArchiveImporter):
  "PackageArchiveImporter subclass that refers to a specific archive."

  def __init__(self, modname, archive_pathname):
    self.__modname = modname
    self.__path = archive_pathname

  def get_archive(self, modname):
    if modname == self.__modname:
      return self.__path
    return None

  # get_subfile is passed the full pathname of the archive


######################################################################
#
# Emulate the standard directory-based import mechanism
#
class DirectoryImporter(imputil.Importer):
  "Importer subclass to emulate the standard importer."

  def __init__(self, dir):
    self.dir = dir

  def get_code(self, parent, modname, fqname):
    if parent:
      dir = parent.__pkgdir__
    else:
      dir = self.dir

    # Return the module (and other info) if found in the specified
    # directory. Otherwise, return None.
    return _fs_import(dir, modname, fqname)

  def __repr__(self):
    return '<%s.%s for "%s" at 0x%x>' % (self.__class__.__module__,
                                         self.__class__.__name__,
                                         self.dir,
                                         id(self))


######################################################################
#
# Emulate the standard path-style import mechanism
#
class PathImporter(imputil.Importer):
  def __init__(self, path=sys.path):
    self.path = path

  def get_code(self, parent, modname, fqname):
    if parent:
      # we are looking for a module inside of a specific package
      return _fs_import(parent.__pkgdir__, modname, fqname)

    # scan sys.path, looking for the requested module
    for dir in self.path:
      if isinstance(dir, _StringType):
        result = _fs_import(dir, modname, fqname)
        if result:
          return result

    # not found
    return None

######################################################################

def _test_dir():
  "Debug/test function to create DirectoryImporters from sys.path."
  imputil.ImportManager().install()
  path = sys.path[:]
  path.reverse()
  for d in path:
    sys.path.insert(0, DirectoryImporter(d))
  sys.path.insert(0, imputil.BuiltinImporter())

def _test_revamp():
  "Debug/test function for the revamped import system."
  imputil.ImportManager().install()
  sys.path.insert(0, PathImporter())
  sys.path.insert(0, imputil.BuiltinImporter())