On Fri, Oct 01, 2021 at 03:35:26PM -0400, Jonathan Crall wrote:
My motivation for writing this suggestion is in an attempt to stop a common anti-pattern, where instead of defining a `main` function (or a function by any other name) an simply calling that by adding the above two lines, a lot of Python users I work with will just start dumping their logic into the global scope of the module.
Needless to say, this can have consequences.
What sort of consequences are you thinking of, under what circumstances? Remember, Python is first and foremost a scripting language. There's nothing wrong with writing scripts that run in the global scope.
If there was some default builtin, let's call it `__main__` for now (open to suggestions), that took a function as an argument and conditionally executed it if `__name__ == "__main__"` in the caller's scope, that would allow us to simplify the above boilerplate to a single line with no extra indentation:
``` def main(): ...
__main__(main) ```
You don't need `__main__` to be built into the interpreter. It's a two-line pattern: not every two line function needs to be a builtin. ``` def __main__(func): if __name__ == '__main__': func() ``` Put that in a module, then: ``` from module import __main__ ``` in all your scripts. If you're worried about keystrokes, and your editor doesn't have a "snippets" functionality, you can put your `__main__` in your PYTHONSTARTUP file or sitecustomize.py and have it loaded automatically into builtins. https://pymotw.com/3/site/ ``` import builtins builtins.__main__ = __main__ ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope.
There is nothing necessarily wrong with logic that impacts the global scope in a script. It can be the easiest thing that works. The more complex your system, the more there is to go wrong. Is your definition of simpler based on counting keystrokes? The underlying complexity is the same. Your builtin `__main__` performs exactly the same conditional test, it's just buried behind a function call.
Furthermore, it could be used as a decorator (and the use-case wouldn't be unreasonable!), and we all know how much new Python users love decorators when they find out about them.
Are you being sarcastic? The idiom "We all know how much X love Y" is typically (but not always) used sarcastically. Decorator syntax has been a great success for Python, but even to this day newbies often struggle to understand them and even some experienced users avoid them. My completely unscientific, pluck-a-figure-out-of-the-air guestimate is that about 10% of Python users treat decorators as a hammer and every problem they see as a nail, another 10% find them to be utterly perplexing and a source of dread, 20% know how to cut-and-paste a recipe involving decorators when needed, but don't know how they work (and have no interest in learning), and the rest have some reasonable level of understanding of how to use decorators and write their own. In this particular case, using decorator syntax: @__main__ def main(): ... to call the function immediately, rather than the more straightforward __main__(main) seems to me to fall squarely in the "hammer + nail" category. I can't tell whether this is a serious proposal on your part or not.
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
There is no need to discourage globals for script writing. And I'm not entirely sure how it follows that encouraging people to use a decorator to run their script main function will discourage them from using decorators everywhere. What have I misunderstood? -- Steve