[Import-SIG] Idea: Autorun functionality for Python modules (redux)
Petr Viktorin
encukou at gmail.com
Tue May 23 06:47:52 EDT 2017
On 05/23/2017 04:02 AM, Nick Coghlan wrote:
> (Note: Posting to import-sig as this isn't something I'm actively
> planning to pursue myself any time soon, but I want to ensure we don't
> accidentally block this possibility while working on the proposal to
> make it possible to run extension modules as Python scripts. )
Thanks for sharing another piece of the master plan :)
> PEP 299 is an old rejected PEP proposing a special "__main__()"
> function for Python modules: https://www.python.org/dev/peps/pep-0299/
>
> Three main points were cited in its rejection:
>
> - the name clash with "import __main__"
> - the lack of a clear strategy for supporting both newer versions of
> Python that supported automatic execution of a suitably named function
> as well as older versions that required an "if __name__ ==
> '__main__':" block
> - the status quo wasn't seen as particular broken and "it would be
> more familiar to C/C++ programmers" wasn't a compelling argument for
> adding a second way to do it
>
> In an email discussion with Brandon Rhodes a few months back, he
> lamented the apparent intransigence of the core developers on this
> front, and I pointed out that nobody had ever actually made a
> follow-up proposal that specifically addressed the rationale applied
> in rejecting PEP 299, and put together a sketch of what such a
> proposal might look like.
>
> The first two technical points can be handled by:
>
> 1. Using `__run__` as the special function name
> 2. Setting "__main__.__autorun__ = True" prior to main module
> execution, and allowing a module to delete it or set `__autorun__ =
> False` to turn off the default autorun behaviour
>
> With those two special attributes defined, the autorun protocol would be:
>
> if getattr(main_module, "__autorun__", False):
> try:
> runmain = main_module.__run__
> except AttributeError:
> pass
> else:
> import sys
> sys.exit(runmain(sys.argv)
>
> Scripts that want to optionally invoke "__run__" explicitly for
> compatibility with older Python versions can then check "__autorun__"
> to see whether or not they need to start the application themselves:
>
> if __name__ == "__main__" and not globals().get("__autorun__"):
> import sys
> sys.exit(__run__(sys.argv))
I have two nitpicks:
1) __main__.__autorun__ is quite a weird place to store
interpreter-level configuration.
2) AFAIU, `if __name__ == "__main__"` is an obscure implementation
detail (answer to "what should be the name of a nameless module?"),
reified by people relying on it. It would be nice to try to downgrade it
to being just an implementation detail again.
So instead of __main__.__autorun__, I'd prefer:
2. Setting `sys.autorun_module = "__main__"` prior to main module
execution, and allowing a module to change it to use another module, or
delete it to turn off the default autorun behaviour.
The autorun protocol would become:
main_module_name = getattr(sys, 'autorun_module', None)
if main_module_name is not None:
main_module = __import__(main_module_name)
try:
runmain = main_module.__run__
except AttributeError:
pass
else:
import sys
sys.exit(runmain(sys.argv)
and the compatibility shim would be:
if __name__ == getattr(sys, "autorun_module", "__main__"):
import sys
sys.exit(__run__(sys.argv))
(This runs the shim in all Python versions, which I think is fine:
sys.exit would prevent the future Python's own autorun from running.)
More information about the Import-SIG
mailing list