Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How Import Does Its Thing

How Import Does Its Thing

PyCon 2008

Brett Cannon

March 14, 2008
Tweet

More Decks by Brett Cannon

Other Decks in Programming

Transcript

  1. About This Talk Goal is for you to understand how

    import finds a module to import. How to go from source code to module object is beyond this talk. I will cover (planned) Python 3.0 semantics.
  2. from __future__ import absolute_import Allows dots to specify where to

    look for a module relative to your current location in a package. Cannot escape out of a package. No dots represents a top-level import (e.g., directly accessed through sys.path).
  3. Examples Assume foo/bar/spam/bruce directory hierarchy and importing from foo/bar/spam. from

    . import bruce Current level. from .. import spam Parent level.
  4. Statement to Function Call Import statement is syntactic sugar. Calls

    built-in function __import__(). Can override if so desired.
  5. __import__ name Name of module being requested. Could be relative.

    globals Caller’s (i.e., module requesting the import) globals. locals Caller’s locals.
  6. Import Lock Acquired before each import. Prevents parallel imports of

    the same module from different threads since module state is shared between interpreters.
  7. Name Resolution Want to resolve relative import names to be

    absolute. Based on name, globals and level. We care if the caller is a package’s __init__ or not.
  8. Example to Use Caller is foo.bar, which is a package

    . foo/bar/__init__.py . from ..ni import shrubbery . level == 2 name == ni Want to end up with foo.ni .
  9. name, globals, level caller's path = globals['__path__'] caller's name =

    globals['__name__'] caller's path level -= 1 base = Trim caller's name from the right level # of dots name return result result = base + '.' + name result = base True False True False level = 2 caller’s path = [‘/foo/bar’] caller’s name = ‘foo.bar’ base = ‘foo’ level = 1 name = ‘ni’ result = ‘foo.ni’
  10. sys.meta_path Handles importing modules that have no “location”. Built-in modules.

    Frozen modules. Sequence of objects called “importers”.
  11. sys.path Sequence of objects representing “locations”. Pretty much always contains

    strings. Similar to sys.meta_path in terms of importers/ loaders, but has a caching mechanism for importers. For backwards-compatibility, entries on sys.path are not replaced with their representative importer. Not searched if parent module defines __path__. __path__ is used instead.
  12. for entry in search: importer = sys.path_importer_cache[entry] loader = importer.find_module(name)

    return loader.load_module(name) Search path hook True False True False importer raise ImportError
  13. for hook in sys.path_hooks: importer = hook(entry) sys.path_importer_cache[entry] = importer

    sys.path_importer_cache[entry] = dummy path hook importer False True
  14. Overview 1. Function call. 2. Import lock. 3. Resolve name

    to be absolute. 4. sys.modules. 5. sys.meta_path. 6. Parent’s __path__ or sys.path.
  15. The Big Picture ... Literally __import__(name, globals, locals, fromlist, level)

    caller_name = globals['__name__'] caller_path = globals['__path__'] caller_path level -= 1 base = caller_name.rsplit(".", level)[0] name absolute_name = base absolute_name = base + '.' + name absolute_name in sys.modules return sys.modules[absolute_name] for importer in sys.meta_path: loader = importer.find_module(absolute_name, caller_path) loader return loader.load_module(absolute_name) caller_path search = caller_path search = sys.path for entry in search: entry in sys.path_importer_cache importer = sys.path_importer_cache[absolute_name] loader = importer.find_module(absolute_name) loader return loader.load_modlue(absolute_name) for hook in sys.path_hooks importer = hook(entry) sys.path_importer_cache[entry] = importer sys.path_importer_cache[entry] = default_importer raise ImportError False False True True False True True False True False True False ImportError no exception raised False True
  16. Importer 1. Take the tail-end name of the module to

    be imported. 2. Join with the directory being searched in. 3. Look for a __init__.py file. 4. Otherwise look for a file ending in .py, .pyc, .pyo, or .pyw.
  17. Loader 1. Create a module object and set __name__, __file__,

    __loader__ (and __path__ if needed). 2. Get data from file found by the importer. 3. Create a code object. 4. Execute code object in the module namespace.
  18. Bytecode or Source? Bytecode must have a matching magic number.

    If source is also available, timestamp in bytecode must be newer than last modification date on source file. Otherwise use source if available.