runpy.py 5.62 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
"""runpy.py - locating and running Python code using the module namespace

Provides support for locating and running Python scripts using the Python
module namespace instead of the native filesystem.

This allows Python code to play nicely with non-filesystem based PEP 302
importers when locating support scripts as well as when importing modules.
"""
# Written by Nick Coghlan <ncoghlan at gmail.com>
#    to implement PEP 338 (Executing Modules as Scripts)

import sys
import imp
14 15 16 17
try:
    from imp import get_loader
except ImportError:
    from pkgutil import get_loader
18 19 20 21 22 23

__all__ = [
    "run_module",
]


24 25
def _run_code(code, run_globals, init_globals=None,
              mod_name=None, mod_fname=None,
Nick Coghlan's avatar
Nick Coghlan committed
26
              mod_loader=None, pkg_name=None):
27 28 29
    """Helper for _run_module_code"""
    if init_globals is not None:
        run_globals.update(init_globals)
30
    run_globals.update(__name__ = mod_name,
31
                       __file__ = mod_fname,
Nick Coghlan's avatar
Nick Coghlan committed
32 33
                       __loader__ = mod_loader,
                       __package__ = pkg_name)
34 35 36
    exec code in run_globals
    return run_globals

37
def _run_module_code(code, init_globals=None,
38
                    mod_name=None, mod_fname=None,
Nick Coghlan's avatar
Nick Coghlan committed
39
                    mod_loader=None, pkg_name=None):
40 41
    """Helper for run_module"""
    # Set up the top level namespace dictionary
42 43 44 45 46 47 48 49 50 51 52
    temp_module = imp.new_module(mod_name)
    mod_globals = temp_module.__dict__
    # Modify sys.argv[0] and sys.module[mod_name]
    saved_argv0 = sys.argv[0]
    restore_module = mod_name in sys.modules
    if restore_module:
        saved_module = sys.modules[mod_name]
    sys.argv[0] = mod_fname
    sys.modules[mod_name] = temp_module
    try:
        _run_code(code, mod_globals, init_globals,
Nick Coghlan's avatar
Nick Coghlan committed
53 54
                    mod_name, mod_fname,
                    mod_loader, pkg_name)
55 56
    finally:
        sys.argv[0] = saved_argv0
57 58 59 60
        if restore_module:
            sys.modules[mod_name] = saved_module
        else:
            del sys.modules[mod_name]
61 62 63
    # Copy the globals of the temporary module, as they
    # may be cleared when the temporary module goes away
    return mod_globals.copy()
64 65


66 67
# This helper is needed due to a missing component in the PEP 302
# loader protocol (specifically, "get_filename" is non-standard)
68 69
# Since we can't introduce new features in maintenance releases,
# support was added to zipimporter under the name '_get_filename'
70
def _get_filename(loader, mod_name):
71 72 73 74 75
    for attr in ("get_filename", "_get_filename"):
        meth = getattr(loader, attr, None)
        if meth is not None:
            return meth(mod_name)
    return None
76

77 78
# Helper to get the loader, code and filename for a module
def _get_module_details(mod_name):
79
    loader = get_loader(mod_name)
80
    if loader is None:
81 82
        raise ImportError("No module named %s" % mod_name)
    if loader.is_package(mod_name):
83 84 85 86 87 88 89 90
        if mod_name == "__main__" or mod_name.endswith(".__main__"):
            raise ImportError(("Cannot use package as __main__ module"))
        try:
            pkg_main_name = mod_name + ".__main__"
            return _get_module_details(pkg_main_name)
        except ImportError, e:
            raise ImportError(("%s; %r is a package and cannot " +
                               "be directly executed") %(e, mod_name))
91 92
    code = loader.get_code(mod_name)
    if code is None:
93
        raise ImportError("No code object available for %s" % mod_name)
94
    filename = _get_filename(loader, mod_name)
95
    return mod_name, loader, code, filename
96 97 98


# XXX ncoghlan: Should this be documented and made public?
99 100 101
# (Current thoughts: don't repeat the mistake that lead to its
# creation when run_module() no longer met the needs of
# mainmodule.c, but couldn't be changed because it was public)
102 103 104 105 106 107 108
def _run_module_as_main(mod_name, set_argv0=True):
    """Runs the designated module in the __main__ namespace

       These __*__ magic variables will be overwritten:
           __file__
           __loader__
    """
109
    try:
110
        mod_name, loader, code, fname = _get_module_details(mod_name)
111 112 113 114
    except ImportError as exc:
        # Try to provide a good error message
        # for directories, zip files and the -m switch
        if set_argv0:
115
            # For -m switch, just display the exception
116 117 118 119 120 121 122
            info = str(exc)
        else:
            # For directories/zipfiles, let the user
            # know what the code was looking for
            info = "can't find '__main__.py' in %r" % sys.argv[0]
        msg = "%s: %s" % (sys.executable, info)
        sys.exit(msg)
Nick Coghlan's avatar
Nick Coghlan committed
123
    pkg_name = mod_name.rpartition('.')[0]
124 125 126 127
    main_globals = sys.modules["__main__"].__dict__
    if set_argv0:
        sys.argv[0] = fname
    return _run_code(code, main_globals, None,
Nick Coghlan's avatar
Nick Coghlan committed
128
                     "__main__", fname, loader, pkg_name)
129 130 131 132 133 134 135

def run_module(mod_name, init_globals=None,
               run_name=None, alter_sys=False):
    """Execute a module's code without importing it

       Returns the resulting top level namespace dictionary
    """
136
    mod_name, loader, code, fname = _get_module_details(mod_name)
137 138
    if run_name is None:
        run_name = mod_name
Nick Coghlan's avatar
Nick Coghlan committed
139
    pkg_name = mod_name.rpartition('.')[0]
140 141
    if alter_sys:
        return _run_module_code(code, init_globals, run_name,
Nick Coghlan's avatar
Nick Coghlan committed
142
                                fname, loader, pkg_name)
143 144
    else:
        # Leave the sys module alone
Nick Coghlan's avatar
Nick Coghlan committed
145 146
        return _run_code(code, {}, init_globals, run_name,
                         fname, loader, pkg_name)
147 148 149 150 151 152 153 154


if __name__ == "__main__":
    # Run the module specified as the next command line argument
    if len(sys.argv) < 2:
        print >> sys.stderr, "No module specified for execution"
    else:
        del sys.argv[0] # Make the requested module sys.argv[0]
155
        _run_module_as_main(sys.argv[0])