[Subversion] / Importing / README.txt  

View of /Importing/README.txt

Parent Directory | Revision Log
Revision: 2685 - (download)
Mon Aug 23 20:15:57 2010 UTC (13 years, 8 months ago) by pje
File size: 9917 byte(s)
Add change info
Dynamic, Lazy, and Weak Imports with the ``Importing`` Toolkit
==============================================================

NEW in 1.10: ``@whenImported`` can now be used as a decorator for lazy imports

Need to import an object when all you've got is its name?  Need to lazily
import modules, such that they don't actually get loaded until you use them?
Want to have some code in a module that only gets run *if* another module is
imported?  Then you need the "Importing" toolkit.

Installing the toolkit (using ``"easy_install Importing"`` or
``"setup.py install"``) allows you to access the ``peak.util.imports`` module.
This module was previously bundled for years inside of the general PEAK
distribution, but is now available as a standalone module for your convenience.

The "Importing" toolkit does not install or use any special import hooks, and
is compatible with zipfile imports, ``py2exe``, etc.  Lazy and weak imports
should be compatible with almost any import hooks or hacks, as long as they
have reasonable support for the ``reload()`` builtin.  The dynamic import
utilities, however, require only that ``__import__()`` work correctly, and so
should work anywhere that normal Python imports work.

NOTE: The 1.9.2 release uses a new version of setuptools (0.6b3) that
fixes a previous problem with .pth files on Windows with the ``Importing``
egg.  See `more info on how to fix your existing installation
<http://www.eby-sarna.com/pipermail/peak/2006-May/002550.html>`_, if you
experienced this problem with a previous version.

Python 2.3 users: If you are using weak or lazy importing with zipfiles (e.g.
eggs) you *must* have Python **2.3.5**.  Lesser versions of 2.3 have a bug in
the ``reload()`` implementation which prevents correct operation of weak and
lazy imports for modules that are inside zipfiles.

.. contents:: **Table of Contents**

You may access any of the following APIs by importing them from
``peak.util.imports``:


Dynamic Imports
---------------

Sometimes you need to get an object based on a name that you don't know until
runtime.  You could kludge something together with ``exec`` or ``__import__``,
but if you need to be able to reference nested classes, class methods, or
other object attributes, and want to handle packages correctly, you will
want to use one of these routines.

All of these routines accept an optional ``globalDict`` parameter to allow
relative importing; see the section on `Relative/Absolute Importing`_ below
for details.

importString(name [, globalDict])
    Import an item specified by a string

    Example Usage::

        attribute1 = importString('some.module:attribute1')
        attribute2 = importString('other.module:nested.attribute2')

    ``importString()`` imports an object from a module, according to an
    import specification string: a dot-delimited path to an object
    in the Python package namespace.  For example, the string
    ``"some.module.attribute"`` is equivalent to the result of
    ``from some.module import attribute``.

    For readability of import strings, it's sometimes helpful to use a ``:`` to
    separate a module name from items it contains.  It's optional, though,
    as ``importString()`` will convert the ``:`` to a ``.`` internally anyway.

importObject(name_or_object [, globalDict])
    Convert a possible string specifier to an object

    If `name_or_object` is a string or unicode object, this routine will import
    it using ``importString()``.  Otherwise, the passed-in object is returned.

    This routine is useful for APIs that want to accept either a (non-string)
    object, or a string designating the object to be used.  They can simply
    call ``importObject()`` on their input without needing to inspect it.

importSequence(string_or_sequence [, globalDict])
    Convert a string or sequence to a list of objects.

    If `string_or_sequence` is a string or unicode object, it is treated as a
    comma-separated list of names to import using ``importString()``, and a
    list of imported objects is returned.  (Whitespace around the commas is
    permitted.)

    If `string_or_sequence` is not a string but is iterable, this returns a
    list created by calling ``importObject()`` on each element of the sequence.

importSuite(name_or_sequence [, globalDict])
    Convert a string or sequence to a ``unittest.TestSuite``

    This routine is identical to ``importSequence()`` except that a
    ``unittest.TestSuite`` is returned instead of a list.


Relative/Absolute Importing
~~~~~~~~~~~~~~~~~~~~~~~~~~~

All of the above routines take an optional ``globalDict`` second argument in
order to support imports relative to a given package (in which case the
passed-in ``globalDict`` should be a module dictionary in the desired package).
These routines do *not* support Python 2.5 relative import syntax.  All imports
done by these routines are absolute, unless you specify a ``globalDict`` (in
which case they are relative to the module the ``globalDict`` came from,
falling back to absolute if there is no relative match).


Lazy Imports
------------

Sometimes you may want to import a module that isn't needed right away.  A
common solution is to put the import inside of a function, so that it doesn't
occur until the function is called.  However, this is slow, increases startup
time, and it can be a bit harder to tell what's going on.  So the Importing
toolkit provides a way to access a module lazily, such that it can be
"imported" in top level code, but not actually *loaded* until the module is
used in some way.  (And unlike many other lazy importing tools for Python,
this API can be applied to *any* Python module and does not interfere
with normal imports of the targeted module(s), whether they are lazily loaded
before or after the normal imports.)

lazyModule(moduleName)
    Return module `moduleName`, but with its contents loaded "on demand"

    This function returns ``sys.modules[moduleName]``, if present.  Otherwise
    it creates a ``LazyModule`` object for the specified module, caches it
    in 'sys.modules', and returns it.

    ``LazyModule`` is a subclass of the standard Python "module" type, that
    remains empty until an attempt is made to access one of its attributes.
    At that moment, the module is loaded into memory, and any hooks that were
    defined via ``whenImported()`` (See the `"Weak" Imports`_ section below)
    are invoked.

    Note that calling ``lazyModule()`` with the name of a non-existent or
    unimportable module will delay the ``ImportError`` until the moment that
    an access is attempted.  The ``ImportError`` will be repeated *every time*
    an attribute access is attempted on the broken module, until the problem is
    corrected.

    Example usage::

        sdist = lazyModule('distutils.command.sdist')

    This is roughly equivalent to ``import distutils.command.sdist as sdist``,
    except that the real import doesn't take place until/unless you try to
    access some attribute of the ``sdist`` object -- unless of course the
    module was already imported before the above line was executed.


"Weak" Imports
--------------

Sometimes you have code in a module that's only needed to support some
third-party module, that your user(s) may or may not be using.  A common
approach to solving this problem is to try importing the third-party module,
but this may import it unnecessarily, if the program never ends up actually
using the module.

So the Importing toolkit offers a solution by combining lazy module loading
with the ability to have a callback run whenever a named module is actually
used.  The registered callback can be used to import additional support code,
register adapters or services or generic functions, or do whatever else might
be needed to add support when the optional third-party module is imported.  But
the callback will be invoked only as soon as the third-party module is *used*
(i.e. any of its attributes are accessed), and not a moment before.

@whenImported(moduleName)
    Call ``func(module)`` when module named `moduleName` is first used

    `moduleName` must be a fully qualified (i.e. absolute) module name.

    If the named module has already been loaded, the decorated function
    is called immediately.  If the named module is not yet loaded, the
    decorated function is registered as a callback that will be invoked
    when the named module is actually used.

    In either case, the decorated function will be passed one argument:
    the module object named by `moduleName`.  It **must not allow any
    unhandled exceptions to escape**, or it may prevent other registered
    callbacks from running.

    Using this function implies a possible lazy import of the specified module,
    and lazy importing means that any ``ImportError`` will be deferred until
    the module is actually used.

    (Note: in older versions of the Importing toolkit, this function actually
    took a second argument: the callback to be registered or called.  If you
    use it in this fashion, the return value is not the decorated function, but
    is instead the module object or lazy module object named by `moduleName`.)


Deprecated Features
-------------------

For backward-compatibility with PEAK's experimental "module inheritance"
feature, ``peak.util.imports`` has some limited support for logical module
paths, in the ``joinPath()`` function and the optional ``relativePath``
argument to ``lazyModule()``.  There is also a ``getModuleHooks()`` function
that is used by the "module inheritance" system.

Please do not use any of these API features, as I would like to remove them or
make them private as soon as the "module inheritance" feature is removed from
PEAK proper.


Mailing List
------------

Please direct questions regarding this package to the PEAK mailing list; see
http://www.eby-sarna.com/mailman/listinfo/PEAK/ for details.

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help