Back to index

python3.2  3.2.2
runpy.py
Go to the documentation of this file.
00001 """runpy.py - locating and running Python code using the module namespace
00002 
00003 Provides support for locating and running Python scripts using the Python
00004 module namespace instead of the native filesystem.
00005 
00006 This allows Python code to play nicely with non-filesystem based PEP 302
00007 importers when locating support scripts as well as when importing modules.
00008 """
00009 # Written by Nick Coghlan <ncoghlan at gmail.com>
00010 #    to implement PEP 338 (Executing Modules as Scripts)
00011 
00012 import sys
00013 import imp
00014 from pkgutil import read_code
00015 try:
00016     from imp import get_loader
00017 except ImportError:
00018     from pkgutil import get_loader
00019 
00020 __all__ = [
00021     "run_module", "run_path",
00022 ]
00023 
00024 class _TempModule(object):
00025     """Temporarily replace a module in sys.modules with an empty namespace"""
00026     def __init__(self, mod_name):
00027         self.mod_name = mod_name
00028         self.module = imp.new_module(mod_name)
00029         self._saved_module = []
00030 
00031     def __enter__(self):
00032         mod_name = self.mod_name
00033         try:
00034             self._saved_module.append(sys.modules[mod_name])
00035         except KeyError:
00036             pass
00037         sys.modules[mod_name] = self.module
00038         return self
00039 
00040     def __exit__(self, *args):
00041         if self._saved_module:
00042             sys.modules[self.mod_name] = self._saved_module[0]
00043         else:
00044             del sys.modules[self.mod_name]
00045         self._saved_module = []
00046 
00047 class _ModifiedArgv0(object):
00048     def __init__(self, value):
00049         self.value = value
00050         self._saved_value = self._sentinel = object()
00051 
00052     def __enter__(self):
00053         if self._saved_value is not self._sentinel:
00054             raise RuntimeError("Already preserving saved value")
00055         self._saved_value = sys.argv[0]
00056         sys.argv[0] = self.value
00057 
00058     def __exit__(self, *args):
00059         self.value = self._sentinel
00060         sys.argv[0] = self._saved_value
00061 
00062 def _run_code(code, run_globals, init_globals=None,
00063               mod_name=None, mod_fname=None,
00064               mod_loader=None, pkg_name=None):
00065     """Helper to run code in nominated namespace"""
00066     if init_globals is not None:
00067         run_globals.update(init_globals)
00068     run_globals.update(__name__ = mod_name,
00069                        __file__ = mod_fname,
00070                        __cached__ = None,
00071                        __loader__ = mod_loader,
00072                        __package__ = pkg_name)
00073     exec(code, run_globals)
00074     return run_globals
00075 
00076 def _run_module_code(code, init_globals=None,
00077                     mod_name=None, mod_fname=None,
00078                     mod_loader=None, pkg_name=None):
00079     """Helper to run code in new namespace with sys modified"""
00080     with _TempModule(mod_name) as temp_module, _ModifiedArgv0(mod_fname):
00081         mod_globals = temp_module.module.__dict__
00082         _run_code(code, mod_globals, init_globals,
00083                   mod_name, mod_fname, mod_loader, pkg_name)
00084     # Copy the globals of the temporary module, as they
00085     # may be cleared when the temporary module goes away
00086     return mod_globals.copy()
00087 
00088 
00089 # This helper is needed due to a missing component in the PEP 302
00090 # loader protocol (specifically, "get_filename" is non-standard)
00091 # Since we can't introduce new features in maintenance releases,
00092 # support was added to zipimporter under the name '_get_filename'
00093 def _get_filename(loader, mod_name):
00094     for attr in ("get_filename", "_get_filename"):
00095         meth = getattr(loader, attr, None)
00096         if meth is not None:
00097             return meth(mod_name)
00098     return None
00099 
00100 # Helper to get the loader, code and filename for a module
00101 def _get_module_details(mod_name):
00102     loader = get_loader(mod_name)
00103     if loader is None:
00104         raise ImportError("No module named %s" % mod_name)
00105     if loader.is_package(mod_name):
00106         if mod_name == "__main__" or mod_name.endswith(".__main__"):
00107             raise ImportError("Cannot use package as __main__ module")
00108         try:
00109             pkg_main_name = mod_name + ".__main__"
00110             return _get_module_details(pkg_main_name)
00111         except ImportError as e:
00112             raise ImportError(("%s; %r is a package and cannot " +
00113                                "be directly executed") %(e, mod_name))
00114     code = loader.get_code(mod_name)
00115     if code is None:
00116         raise ImportError("No code object available for %s" % mod_name)
00117     filename = _get_filename(loader, mod_name)
00118     return mod_name, loader, code, filename
00119 
00120 # XXX ncoghlan: Should this be documented and made public?
00121 # (Current thoughts: don't repeat the mistake that lead to its
00122 # creation when run_module() no longer met the needs of
00123 # mainmodule.c, but couldn't be changed because it was public)
00124 def _run_module_as_main(mod_name, alter_argv=True):
00125     """Runs the designated module in the __main__ namespace
00126 
00127        Note that the executed module will have full access to the
00128        __main__ namespace. If this is not desirable, the run_module()
00129        function should be used to run the module code in a fresh namespace.
00130 
00131        At the very least, these variables in __main__ will be overwritten:
00132            __name__
00133            __file__
00134            __cached__
00135            __loader__
00136            __package__
00137     """
00138     try:
00139         if alter_argv or mod_name != "__main__": # i.e. -m switch
00140             mod_name, loader, code, fname = _get_module_details(mod_name)
00141         else:          # i.e. directory or zipfile execution
00142             mod_name, loader, code, fname = _get_main_module_details()
00143     except ImportError as exc:
00144         # Try to provide a good error message
00145         # for directories, zip files and the -m switch
00146         if alter_argv:
00147             # For -m switch, just display the exception
00148             info = str(exc)
00149         else:
00150             # For directories/zipfiles, let the user
00151             # know what the code was looking for
00152             info = "can't find '__main__' module in %r" % sys.argv[0]
00153         msg = "%s: %s" % (sys.executable, info)
00154         sys.exit(msg)
00155     pkg_name = mod_name.rpartition('.')[0]
00156     main_globals = sys.modules["__main__"].__dict__
00157     if alter_argv:
00158         sys.argv[0] = fname
00159     return _run_code(code, main_globals, None,
00160                      "__main__", fname, loader, pkg_name)
00161 
00162 def run_module(mod_name, init_globals=None,
00163                run_name=None, alter_sys=False):
00164     """Execute a module's code without importing it
00165 
00166        Returns the resulting top level namespace dictionary
00167     """
00168     mod_name, loader, code, fname = _get_module_details(mod_name)
00169     if run_name is None:
00170         run_name = mod_name
00171     pkg_name = mod_name.rpartition('.')[0]
00172     if alter_sys:
00173         return _run_module_code(code, init_globals, run_name,
00174                                 fname, loader, pkg_name)
00175     else:
00176         # Leave the sys module alone
00177         return _run_code(code, {}, init_globals, run_name,
00178                          fname, loader, pkg_name)
00179 
00180 def _get_main_module_details():
00181     # Helper that gives a nicer error message when attempting to
00182     # execute a zipfile or directory by invoking __main__.py
00183     main_name = "__main__"
00184     try:
00185         return _get_module_details(main_name)
00186     except ImportError as exc:
00187         if main_name in str(exc):
00188             raise ImportError("can't find %r module in %r" %
00189                               (main_name, sys.path[0]))
00190         raise
00191 
00192 
00193 # XXX (ncoghlan): Perhaps expose the C API function
00194 # as imp.get_importer instead of reimplementing it in Python?
00195 def _get_importer(path_name):
00196     """Python version of PyImport_GetImporter C API function"""
00197     cache = sys.path_importer_cache
00198     try:
00199         importer = cache[path_name]
00200     except KeyError:
00201         # Not yet cached. Flag as using the
00202         # standard machinery until we finish
00203         # checking the hooks
00204         cache[path_name] = None
00205         for hook in sys.path_hooks:
00206             try:
00207                 importer = hook(path_name)
00208                 break
00209             except ImportError:
00210                 pass
00211         else:
00212             # The following check looks a bit odd. The trick is that
00213             # NullImporter throws ImportError if the supplied path is a
00214             # *valid* directory entry (and hence able to be handled
00215             # by the standard import machinery)
00216             try:
00217                 importer = imp.NullImporter(path_name)
00218             except ImportError:
00219                 return None
00220         cache[path_name] = importer
00221     return importer
00222 
00223 def _get_code_from_file(fname):
00224     # Check for a compiled file first
00225     with open(fname, "rb") as f:
00226         code = read_code(f)
00227     if code is None:
00228         # That didn't work, so try it as normal source code
00229         with open(fname, "rb") as f:
00230             code = compile(f.read(), fname, 'exec')
00231     return code
00232 
00233 def run_path(path_name, init_globals=None, run_name=None):
00234     """Execute code located at the specified filesystem location
00235 
00236        Returns the resulting top level namespace dictionary
00237 
00238        The file path may refer directly to a Python script (i.e.
00239        one that could be directly executed with execfile) or else
00240        it may refer to a zipfile or directory containing a top
00241        level __main__.py script.
00242     """
00243     if run_name is None:
00244         run_name = "<run_path>"
00245     importer = _get_importer(path_name)
00246     if isinstance(importer, imp.NullImporter):
00247         # Not a valid sys.path entry, so run the code directly
00248         # execfile() doesn't help as we want to allow compiled files
00249         code = _get_code_from_file(path_name)
00250         return _run_module_code(code, init_globals, run_name, path_name)
00251     else:
00252         # Importer is defined for path, so add it to
00253         # the start of sys.path
00254         sys.path.insert(0, path_name)
00255         try:
00256             # Here's where things are a little different from the run_module
00257             # case. There, we only had to replace the module in sys while the
00258             # code was running and doing so was somewhat optional. Here, we
00259             # have no choice and we have to remove it even while we read the
00260             # code. If we don't do this, a __loader__ attribute in the
00261             # existing __main__ module may prevent location of the new module.
00262             main_name = "__main__"
00263             saved_main = sys.modules[main_name]
00264             del sys.modules[main_name]
00265             try:
00266                 mod_name, loader, code, fname = _get_main_module_details()
00267             finally:
00268                 sys.modules[main_name] = saved_main
00269             pkg_name = ""
00270             with _TempModule(run_name) as temp_module, \
00271                  _ModifiedArgv0(path_name):
00272                 mod_globals = temp_module.module.__dict__
00273                 return _run_code(code, mod_globals, init_globals,
00274                                     run_name, fname, loader, pkg_name).copy()
00275         finally:
00276             try:
00277                 sys.path.remove(path_name)
00278             except ValueError:
00279                 pass
00280 
00281 
00282 if __name__ == "__main__":
00283     # Run the module specified as the next command line argument
00284     if len(sys.argv) < 2:
00285         print("No module specified for execution", file=sys.stderr)
00286     else:
00287         del sys.argv[0] # Make the requested module sys.argv[0]
00288         _run_module_as_main(sys.argv[0])