Python 2.4 provides the '-m' option to run a module as a script. However, "python -m <script>" will report an error if the specified script is inside a package.
Putting the following code in a module called "execmodule.py" and placing it in a directory on sys.path allows scripts inside packages to be executed using "python -m execmodule <script>".
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
"""Run a module as if it were a file Allows Python scripts to be located and run using the Python module namespace instead of the native filesystem. """ from imp import find_module, PY_SOURCE, PY_COMPILED import sys __all__ = ['execmodule'] class _ExecError(ValueError): pass def execmodule(module_name, globals=None, locals=None, set_argv0 = False): """Locate the requested module and run it using execfile Any containing packages will be imported before the module is executed. Globals and locals arguments are as documented for execfile set_argv0 means that sys.argv will be set to the module's filename prior to execution (some scripts use argv to determine their location). """ if globals is None: globals = sys._getframe(1).f_globals # Mimic execfile behaviour if locals is None: locals = globals pkg_name = None path = None split_module = module_name.rsplit('.', 1) if len(split_module) == 2: module_name = split_module pkg_name = split_module try: # Import the containing package if pkg_name: pkg = __import__(pkg_name) for sub_pkg in pkg_name.split('.')[1:]: pkg = getattr(pkg, sub_pkg) path = pkg.__path__ # Locate the module module_info = find_module(module_name, path) except ImportError, e: raise _ExecError(str(e)) # Check that all is good module = module_info filename = module_info filetype = module_info if module: module.close() # We don't actually want the file handle if filetype not in (PY_SOURCE, PY_COMPILED): raise _ExecError("%s is not usable as a script\n (File: %s)" % (module_name, filename)) # Let's do it if set_argv0: sys.argv = filename execfile(filename, globals, locals) if __name__ == "__main__": if len(sys.argv) < 2: print >> sys.stderr, "No module specified for execution" del sys.argv # Make the requested module sys.argv try: execmodule(sys.argv, set_argv0 = True) except _ExecError, e: print >> sys.stderr, e
Python 2.4 brings us the '-m' command line option to make it easy to run modules like the profiler or debugger as scripts (using "python -m profile <script>" and "python -m pdb <script>" respectively).
Unfortunately, -m is limited to top level modules only - it can't look inside packages. With execmodule in place, it becomes possible to do things like "python -m execmodule pychecker.checker <script>".
The given implementation also makes the execmodule functionality available from Python code - "from execmodule import execmodule" will give a function 'execmodule' with an interface similar to that for the existing builtin 'execfile'. The difference is that 'execmodule' attempts to locate its argument using the Python module namespace instead of the native filesystem. (This equivalence to execfile is what demands the sys._getframe hack - we want to use the caller's namespace when no globals are provided. For those that dislike sys._getframe, remove this line and make the globals argument mandatory)
Note that, due to a limitation of imp.find_module, 'execmodule' cannot use the system metapath to find modules (e.g. inside zip files). However, files in such locations are unlikely to be usable as scripts, even if they could be located.
(This is a much expanded version of a script originally posted to python-dev by Guido van Rossum. Guido's version was equivalent to the current behaviour of '-m' - it was posted during the discussion on whether or not to implement the command line switch)