If you keep your application's front-end execution logic in a __main__.py
file within your package and change the XXX
string to the name of your application's package, this script will handle executing your application for you just like how Python's -m
option does it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #!/usr/bin/env python
import os
import runpy
PKG = 'XXX' # TODO Change to the name of your package (not your executable!).
try:
run_globals = runpy.run_module(PKG, run_name='__main__', alter_sys=True)
executed = os.path.splitext(os.path.basename(run_globals['__file__']))[0]
if executed != '__main__': # For Python 2.5 compatibility
raise ImportError('Incorrectly executed %s instead of __main__' %
executed)
except ImportError: # For Python 2.6 compatibility
runpy.run_module('%s.__main__' % PKG, run_name='__main__', alter_sys=True)
|
You might be wondering why should you use this script over a much simpler file that does nothing more than import your main()
function from some specific module within your application's package? The answer to that question is portability. By forcing you to keep your code in a __main__.py
file, you support execution of your application using Python's -m
option which is supporting starting in Python 2.7. And if you name this script __main__.py
you can then zip up this file along with your application's code (if it is only pure Python source) and you now have a zip file you can pass to Python (2.6 and newer) that can be executed. You can even put the __main__.py
file next to your application's package directory and Python (2.6 and newer) can execute the directory.
In other words, this script recipe forces you to practice proper portability for your application to keep it future-proof in terms of Python's wide-ranging support on how to execute code. More details of the importance of this can be found in this blog post.
This recipe appears to share the original implementation flaw that led to the creation of the private runpy._run_module_as_main function: it breaks -i introspection, since the bulk of the main module code is actually running inside the temporary module created by run_module rather than the real __main__ module created by the interpreter.
If the main module blows up, run_globals never gets set and you can't dig into any details of the program state that existed before the execution failed.
Allowing use cases along the lines of this recipe has tempted me to make _run_module_as_main public at various times, but I've never pulled the trigger on actually doing it.