Python __main__ function
Hi all, In Python we often use the following syntax to call the main logic of script when it was ran: ```python def main(): pass # whatever should be done for `python ./script.py` if __name__ == '__main__': main() ``` Maybe it is a time to introduce the new module level function like __main__ ? Consider the following code: ```python def __main__(): pass # whatever should be done for `python ./script.py` ``` It is much easy an less code to write ;) Under-hood `Python` just will generate the following code: ```python def __main__(): pass # whatever should be done for `python ./script.py` # Below generated code if __name__ == '__main__': __main__() ``` If there are two `if __name__ == '__main__':` it is also not an issue: ```python def __main__(): pass # whatever should be done for `python ./script.py` def main(): pass # whatever should be done for `python ./script.py` if __name__ == '__main__': main() # Below generated code if __name__ == '__main__': __main__() ``` Or we could require user to have only one `if __name__ == '__main__':` ... What do you think, please share your opinion ...
redradist@gmail.com wrote:
While is possible to use if __name__ == '__main__': several times in the same script, your proposed magic function def __main__() cannot be redefined. Not to speak about lexical scope differences between one approach and the other. So your proposal is reducing features instead of expanding them. (Sorry for repeating the message. The first one went to another thread)
Hello, On Thu, 28 May 2020 09:06:36 -0000 redradist@gmail.com wrote:
For as long as it works (it does), there's no need to introduce anything. However, if you want to add special features, adding __main__() can be useful. For example, for Python "strict mode" (which I hope to post for beating to this list one of these years), I need to explicitly separate "load time" from "run time" of a Python program. And to achieve that, I exactly introduce a "main" function, and even call it __main__ (so the name is taken!!111 ;-) ). And in all fairness, all good ideas already came to somebody else years ago. There's https://www.python.org/dev/peps/pep-0299/ , successfully rejected yet back in 2002. (So, feel free to use it in your own environment/Python dialect.) [] -- Best regards, Paul mailto:pmiscml@gmail.com
On Thu, May 28, 2020 at 12:57 PM Paul Sokolovsky <pmiscml@gmail.com> wrote:
Thanks for the link. Pity it was rejected. I can think of one reason I'd quite like this beyond it being DRY and concise: I often see code written by others like this: ``` def foo(bar): print(bar) if __name__ == '__main__': bar = 3 foo(bar) ``` Now `bar` is both a local and global variable, which leads to both annoying IDE warnings and actual bugs. I think I encountered a bug related to this when someone used `eval()` (which was the right thing to do in this case) and didn't specify the namespaces correctly, but name shadowing made it seem like their code was correct. Point is, I'd like something that encourages people to put all their `__main__` logic and variables into a function, rather than a module level conditional.
On Fri, May 29, 2020 at 5:25 AM Alex Hall <alex.mojaki@gmail.com> wrote:
People can already put all their main logic into a function. If you want to unit-test your main function, that's the best way to do it. The trouble is, how much goes into main() and how much into if __name__ == '__main__'? For example: Does your main function accept a list of arguments, or does it look in sys.argv? Neither answer is wrong. And that means the best way is to NOT force everyone across all of Python to choose the same way - let people write their code their way. ChrisA
Hello, On Fri, 29 May 2020 05:33:57 +1000 Chris Angelico <rosuav@gmail.com> wrote: []
Yes. Besides, "principled" and "uncompromising" linters and code formatters are recently on a rise, so hopefully everyone/every team can choose a style per their likes, without putting burden on all the other users of the language.
ChrisA
-- Best regards, Paul mailto:pmiscml@gmail.com
On Thu, May 28, 2020 at 9:52 PM Paul Sokolovsky <pmiscml@gmail.com> wrote:
Yes, and I want to make it easy, even tempting, to do so.
I would suggest that people write another function (maybe main without the dunder) if they want something a little more custom (e.g. more arguments) and have `__main__` call that. Really the line `def __main__(...):` would almost mechanically replace `if __name__ == '__main__':`, so it's easy to look at existing projects to see how this could be handled. They could also give `__main__` optional arguments.
There is no forcing, there is no burden. Certainly the `if` method should remain for those who need it (e.g. to check in two separate blocks) and for compatibility. It would just encourage better scoping. Besides, it would be applicable in the vast majority of cases.
(Apologies to Chris, reply vs. replay all error on my part)
People write main entry points that are not exactly this? If __name__ == '__main__': sys.exit(main(sys.argv[1:])) Which is not to say this is the only use of the idiom about __name__ and __main__, just that the community appears to be slowly converging on some spelling of that. I would be ok with some sugar for that spelling, but I don't need it. I would not be ok with getting rid of the current spelling.
Hello, On Thu, 28 May 2020 16:05:52 -0400 <tritium-list@sdamon.com> wrote: []
Yes, most of the time, I don't emulate C main function, so I write it as: if __name__ == "__main__": main() (Mind the double quotes!!) And one sweet day, I intend to fully convert to write it: if __name__ == "__main__": __main__() (The morale of the story: you won't get all people agree to your way, can as well accept that.) -- Best regards, Paul mailto:pmiscml@gmail.com
On 28/05/2020 21:05, tritium-list@sdamon.com wrote:
Mostly I don't write main entry points at all. If I do the dance, it's more likely to be: if __name__ == '__main__': test_this_module_to_destruction() ...though personally I prefer separate scripts for testing. For the main program, I'm generally using argparse so there's no need to mess with sys.argv directly, and why on earth would I add complexity and indentation with an entirely unnecessary function wrapper? -- Rhodri James *-* Kynesim Ltd
On Thu, May 28, 2020 at 9:03 PM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
If all you care about in terms of exit codes is 0 for success and 1 for error (which is sufficient for most scripts), then yes, this is probably not needed. However, it's useful in two cases. One is if you're writing a script tool to be distributed, where you probably want to have a blanket "except Exception" clause in main() to catch everything and print a graceful failure message. In that case, you probably want to suppress the ugly traceback, which requires manually producing the appropriate exit code (probably 1, but maybe something else). Additionally, you might be writing a tool with more complex exit codes that are part of its documented API. A few GNU tools are like this ("diff" is the first example that comes to mind). Especially if you want to write a (mostly) drop-in replacement for such a tool, mimicking the exit code behavior can be critical. So this type of entry point is useful even in Python, and tools that generate entry point scripts* should continue to generate code like this. It works even if you never put a return statement in main(), since the script exits on its own with 1 on an unhandled exception, and the implicit "return None" at the end of main() is treated by sys.exit as 0. *pip, for example, does this. It's worth noting that most instances I've seen of this have been automatically generated scripts like those; I've rarely seen a handwritten example. And most cases of this that I see, including pip's scripts, don't pass anything to main(), because you can always get the argv from the sys module (and sys.argv[0] can be useful in some cases).
On Fri, May 29, 2020 at 12:43 PM Jonathan Goble <jcgoble3@gmail.com> wrote:
But thanks to exception handling, we can unwind the stack in a perfectly clean manner AND specify a return value, all in one simple operation: sys.exit(4) You don't need to carry the return value from main. You don't have to worry about how deep into your call stack the decision to exit is. You just sys.exit with whatever value you want, and everything will be cleaned up perfectly. So the logic can simply be: If main returns, then it was successful, and we exit zero. If main raises an exception, then it was unsuccessful, and we exit one, or some other value if specified by the exception. And that's spelled: if __name__ == '__main__': main() ChrisA
Also, I routinely write scripts that have no `if __name__ == '__main__'` line at all, they just run - no-one should ever import them, so it makes no difference. And I exit (in multiple places) using `raise SystemExit("reason")`. My point being that yes, there are *lots* of ways of writing Python scripts/programs. Why "bless" one of them as being somehow superior? Paul On Fri, 29 May 2020 at 02:02, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
I agree with Paul's sentiment. We do not need to bless just one way of writing scripts. Code review or organization style guides, sure. But not at language level. I also write scripts with no explicit __main__. But I rarely name them as .py. In the style of Unix commands, they have no extension, but have a pound-bang at the start. This has the side effect of making them much less straightforward to import at all. On Fri, May 29, 2020, 3:29 AM Paul Moore <p.f.moore@gmail.com> wrote:
On 2020-05-28 18:02, Greg Ewing wrote:
If you'd like a script to be uhh, highly-scriptable, returning one of the os.EX_* status codes can be useful to communicate back to the calling shell what happened. First catch the exceptions, convert to the appropriate status code, then return at the end. -Mike
On Sun, May 31, 2020 at 6:14 AM Mike Miller <python-ideas@mgmiller.net> wrote:
OR! You could just do nothing. And then, if SystemExit is raised, Python will return that exit code to the calling process (whether it's a shell or not). Python even provides a convenient function sys.exit for raising SystemExit with a specific code... Don't catch what you can't deal with. ChrisA
On Sun, May 31, 2020 at 06:45:01AM +1000, Chris Angelico wrote:
Well, that's very fine and good, but the chances are remote that SystemExit will be raised if I do nothing. More likely it will be some other exception. In order to raise SystemError, you need to catch the exception that actually occurred, convert it to an os.EX_* error code, and at that point it's entirely up to you whether you return from main() and let the sys.exit() wrapper handle the exit, or call sys.exit directly. Here's my preference: a function can have many returns, and that's fine. But an application should only have one (or at most a few) exit points, and they should never, ever be buried inside a function that someone might want to call. What's wrong with this test code? It's buggy. Can you see why? unittest.main() doctest.testmod(__main__)
I'm not entirely sure, but I *think* that both "tritium-list" and Mike may be aware of this, since they are calling sys.exit to do precisely that *wink*
Don't catch what you can't deal with.
That's good advice, but how does it apply here since Mike clearly can deal with the exceptions. He even tells you what he does: "First catch the exceptions, convert to the appropriate status code, then return at the end." Which seems like excellent advice to me. -- Steven
Chris Angelico wrote:
It is not enforcing user it just to provide more choice: ```python if __name__ == '__main__': ... main logic ... ``` or this way: ```python def __main__(); sys.argv[0] ... main logic ... ``` `__main__` will be called only once at the end of script if it was called as script Also on additional benefit, it will help people that came from other languages: ```rust fn main() { ... } ``` or ```c void main() { ... } ``` or ```java class MyClass { public void main(...) { ... } } ``` and in Python it could look like this: ```python def __main__(); ... main logic ... ```
participants (14)
-
Alex Hall
-
Chris Angelico
-
David Mertz
-
Ethan Furman
-
Greg Ewing
-
jdveiga@gmail.com
-
Jonathan Goble
-
Mike Miller
-
Paul Moore
-
Paul Sokolovsky
-
redradist@gmail.com
-
Rhodri James
-
Steven D'Aprano
-
tritium-list@sdamon.com