[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