blob: 6c44850f84bbd613aa565737e304ff54bf697a2d [file] [log] [blame]
Nick Coghlane2ebb2d2006-03-15 11:00:26 +00001"""runpy.py - locating and running Python code using the module namespace
2
3Provides support for locating and running Python scripts using the Python
4module namespace instead of the native filesystem.
5
6This allows Python code to play nicely with non-filesystem based PEP 302
7importers when locating support scripts as well as when importing modules.
8"""
9# Written by Nick Coghlan <ncoghlan at gmail.com>
10# to implement PEP 338 (Executing Modules as Scripts)
11
12import sys
13import imp
Phillip J. Ebyab1d2452006-04-17 20:17:25 +000014try:
15 from imp import get_loader
16except ImportError:
17 from pkgutil import get_loader
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000018
19__all__ = [
20 "run_module",
21]
22
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000023
Nick Coghlan999a3362006-06-28 10:41:47 +000024def _run_code(code, run_globals, init_globals, run_name,
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000025 mod_name, mod_fname, mod_loader):
26 """Helper for _run_module_code"""
27 if init_globals is not None:
28 run_globals.update(init_globals)
Nick Coghlan999a3362006-06-28 10:41:47 +000029 run_globals.update(__name__ = run_name,
30 __module_name__ = mod_name,
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000031 __file__ = mod_fname,
32 __loader__ = mod_loader)
33 exec code in run_globals
34 return run_globals
35
Nick Coghlan999a3362006-06-28 10:41:47 +000036def _run_module_code(code, init_globals=None, run_name=None,
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000037 mod_name=None, mod_fname=None,
38 mod_loader=None, alter_sys=False):
39 """Helper for run_module"""
40 # Set up the top level namespace dictionary
41 if alter_sys:
42 # Modify sys.argv[0] and sys.module[mod_name]
43 temp_module = imp.new_module(mod_name)
44 mod_globals = temp_module.__dict__
45 saved_argv0 = sys.argv[0]
Nick Coghlan999a3362006-06-28 10:41:47 +000046 sentinel = object()
47 module_mod_name = sys.modules.get(mod_name, sentinel)
48 module_run_name = sys.modules.get(run_name, sentinel)
Nick Coghlanc841bb62006-03-24 13:05:53 +000049 sys.argv[0] = mod_fname
50 sys.modules[mod_name] = temp_module
Nick Coghlan999a3362006-06-28 10:41:47 +000051 if run_name != mod_name:
52 sys.modules[run_name] = temp_module
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000053 try:
Nick Coghlan999a3362006-06-28 10:41:47 +000054 _run_code(code, mod_globals, init_globals, run_name,
Nick Coghlanc841bb62006-03-24 13:05:53 +000055 mod_name, mod_fname, mod_loader)
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000056 finally:
Nick Coghlanc841bb62006-03-24 13:05:53 +000057 sys.argv[0] = saved_argv0
Nick Coghlan999a3362006-06-28 10:41:47 +000058 if module_mod_name is not sentinel:
59 sys.modules[mod_name] = module_mod_name
60 else:
61 del sys.modules[mod_name]
62 if run_name != mod_name:
63 if module_run_name is not sentinel:
64 sys.modules[run_name] = module_run_name
65 else:
66 del sys.modules[run_name]
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000067 # Copy the globals of the temporary module, as they
68 # may be cleared when the temporary module goes away
69 return mod_globals.copy()
70 else:
71 # Leave the sys module alone
Nick Coghlan999a3362006-06-28 10:41:47 +000072 return _run_code(code, {}, init_globals, run_name,
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000073 mod_name, mod_fname, mod_loader)
74
75
Phillip J. Ebyab1d2452006-04-17 20:17:25 +000076# This helper is needed due to a missing component in the PEP 302
77# loader protocol (specifically, "get_filename" is non-standard)
78def _get_filename(loader, mod_name):
79 try:
80 get_filename = loader.get_filename
81 except AttributeError:
82 return None
83 else:
84 return get_filename(mod_name)
85
86
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000087def run_module(mod_name, init_globals=None,
88 run_name=None, alter_sys=False):
89 """Execute a module's code without importing it
Tim Petersf99b8162006-03-15 18:08:37 +000090
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000091 Returns the resulting top level namespace dictionary
92 """
Phillip J. Ebyab1d2452006-04-17 20:17:25 +000093 loader = get_loader(mod_name)
Nick Coghlane2ebb2d2006-03-15 11:00:26 +000094 if loader is None:
95 raise ImportError("No module named " + mod_name)
96 code = loader.get_code(mod_name)
97 if code is None:
98 raise ImportError("No code object available for " + mod_name)
99 filename = _get_filename(loader, mod_name)
100 if run_name is None:
101 run_name = mod_name
Tim Petersf99b8162006-03-15 18:08:37 +0000102 return _run_module_code(code, init_globals, run_name,
Nick Coghlan999a3362006-06-28 10:41:47 +0000103 mod_name, filename, loader, alter_sys)
Nick Coghlane2ebb2d2006-03-15 11:00:26 +0000104
105
106if __name__ == "__main__":
107 # Run the module specified as the next command line argument
108 if len(sys.argv) < 2:
109 print >> sys.stderr, "No module specified for execution"
110 else:
111 del sys.argv[0] # Make the requested module sys.argv[0]
112 run_module(sys.argv[0], run_name="__main__", alter_sys=True)