Syntax Sugar for __name__ == "__main__" boilerplate?
I was curious if / what sort of proposals have been considered for simplifying the pattern: ``` def main(): ... if __name__ == "__main__": main() ``` I imagine this topic must have come up before, so I'd be interested in any relevant history. But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering. 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. 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) ``` In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for. 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. ``` @__main__ def main(): ... ``` Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system. -- -Dr. Jon Crall (him)
How about the following? def __main__(): ... Behavior: 1. Load module as normal. 2. If __name__ is "__main__" or module is named in python -m, call __main__ function. Paul On Fri, 2021-10-01 at 15:35 -0400, Jonathan Crall wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N... Code of Conduct: http://python.org/psf/codeofconduct/
That would also make a lot of sense. That way has fewer characters without sacrificing clarity, which is a +1 in my book. On Fri, Oct 1, 2021 at 3:38 PM Paul Bryan <pbryan@anode.ca> wrote:
How about the following?
def __main__():
...
Behavior:
1. Load module as normal. 2. If __name__ is "__main__" or module is named in python -m, call __main__ function.
Paul
On Fri, 2021-10-01 at 15:35 -0400, Jonathan Crall wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N... Code of Conduct: http://python.org/psf/codeofconduct/
-- -Dr. Jon Crall (him)
On Fri, Oct 01, 2021 at 03:40:54PM -0400, Jonathan Crall wrote:
That would also make a lot of sense. That way has fewer characters without sacrificing clarity, which is a +1 in my book.
On Fri, Oct 1, 2021 at 3:38 PM Paul Bryan <pbryan@anode.ca> wrote:
How about the following?
def __main__():
...
+1000! This is very clean and natural. It fits nicely in the python naming model, because the double-dunder name is formally reserved. A nice bonus is that this scheme is very close to main() in C/C++/Java and other compiled languages, so users coming in from those languages will understand this without further explanation. (If this proposal were accepted, I think it'd make sense to introduce a __future__.main_function that'd enable this functionality as opt-in first, and only later as the default.)
Behavior:
1. Load module as normal. 2. If __name__ is "__main__" or module is named in python -m, call __main__ function.
Zbyszek
On Sat, Oct 02, 2021 at 09:22:04AM +0000, Zbigniew Jędrzejewski-Szmek wrote:
A nice bonus is that this scheme is very close to main() in C/C++/Java and other compiled languages, so users coming in from those languages will understand this without further explanation.
I don't think they will actually understand it. What they might do is be fooled into *thinking* that they understand it, in C terms, when they don't. The entry point to a Python script is not main(), it is the top of the module. It is merely a convention that we sometimes write a main function, and call it at the bottom. And not even a strong convention: plenty of people write Python scripts with no main() function and no need to test for `if __name__ == __main__`. And there is nothing wrong with that. In C terms, this code is not even possible: print("Hello") def main(): print("Goodbye") __main__(main) print("Why are you still here? Go home!") since you cannot have code outside of a function that is called before the entry point main() (declarations and macros excempted) or after it returns. I don't think it is a good idea to give programmers coming from other languages the false impression that Python behaves the same as the language they are familiar with, when it doesn't. The entrypoint to Python scripts is not main(), it is the top of the module. -- Steve
plenty of people write Python scripts with no main() function and no need to test for `if __name__ == __main__`.
And there is nothing wrong with that.
Indeed, I think there is something “right” with that. The if __name__ block is only required for a Python file to be both a module and a script. That’s actually a pretty uncommon thing— if it’s a module to be imported by other modules, then it probably should be part of a package, and if the functionality needs to be made available as a script, there can be another module just for that. If a script is complicated enough that it should be built as multiple functions, it can call the main function after define ing what’s needed, but no need for the __main__ guard. Someone in this thread (the OP?) wrote that most of the modules they right are both importable and scripts — I’d like to hear more about those use cases, because that diste seems like a bad practice to me. -CHB
In C terms, this code is not even possible:
print("Hello")
def main(): print("Goodbye")
__main__(main) print("Why are you still here? Go home!")
since you cannot have code outside of a function that is called before the entry point main() (declarations and macros excempted) or after it returns.
I don't think it is a good idea to give programmers coming from other languages the false impression that Python behaves the same as the language they are familiar with, when it doesn't. The entrypoint to Python scripts is not main(), it is the top of the module.
-- Steve _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/NRSVRV... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
Perhaps more people need to be made aware of the __main__.py package module feature we have in Python: https://docs.python.org/3/library/__main__.html Instead of just a single main() function, you get a whole module to play with :-) -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Oct 03 2021)
Python Projects, Coaching and Support ... https://www.egenix.com/ Python Product Development ... https://consulting.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 https://www.egenix.com/company/contact/ https://www.malemburg.com/
On Sun, Oct 3, 2021, 1:46 AM Christopher Barker
The if __name__ block is only required for a Python file to be both a module and a script. That’s actually a pretty uncommon thing— if it’s a module to be imported by other modules, then it probably should be part of a package, and if the functionality needs to be made available as a script, there can be another module just for that.
I think all the slightly different boilerplate forms suggested are needless. So on the main topic, -1. But as a note, I usually put a __name__=='__main__' block at the foot of my library code. The few functions in this particular file presumably do something useful, and hopefully composable. Why not allow command-line users to do just that one thing by itself? Sure, the predominant use is within a library, but being able to try out this functionality usually helps my development, and is sometimes useful to have around and composable in bash.
@Marc-Andre One of the motivations of this proposal is to incentivize writing code in a local scope, rather than a global one. People in this thread have made the argument "Python is a scripting language, so it should do that". Ok, fine. Yes `print('hello world')` is more idiomatic than the one inside of a function, but any real work done in Python quickly discards that global simplicity. Variable assignment in a global scope is slower than the equivalent code in a local scope, due to the STORE_FAST bytecode being used inside a function and STORE_NAME being used in the global scope: https://stackoverflow.com/questions/11241523/why-does-python-code-run-faster... And it is true that this isn't a huge performance hit, and it is entirely avoidable by simply adding the ``` def main(): ... if __name__ == '__main__': main() ``` boilerplate yourself, but it feels inelegant to me. I suppose it is a subtle reminder that Python is a scripting language, and we are coercing it into a library. So I can understand that argument. One of the motivations for me posting this was a several students I was working with were putting all of their logic in the `if __name__ == '__main__'` block, defining global variables in them, and then relying on those global variable existing in other places in the code. This did what they expected when they were running their code as a script, but when I tried to import it as a module, I immediately encountered errors. My thought is that these errors would have been more obvious to the students if they were writing their logic (context is training a neural network) in a main function, rather than in the global scope. @Christopher Barker
The if __name__ block is only required for a Python file to be both a module and a script. That’s actually a pretty uncommon thing— if it’s a module to be imported by other modules, then
I suppose I do an uncommon thing fairly often. I work with neural networks and I generally construct a package with a pkg/fit.py pkg/predit.py and pkg/evaluate.py which all operate as both a module and a script. I'll run `python -m pkg.fit [args]` to train a network but I'll also `from pkg import fit`, especially when testing. I think if you are testing an executable module you almost always want to have part of it be importable so it can be unit tested. And I think the natural argument against that here, is, well why don't you put that in a `__main__.py`? To which my response is that `pkg` has multiple entry points, so that would require some sort of modal boilerplate, which is useful in many cases and I have done it, but it works against the simplicity of the case where it makes intuitive sense that the package module should be accessible as a module and a library. Retrospectively, I would agree that this is probably uncommon, but if nothing else comes out of this discussion, I would want to encourage Python users to write Python files as both modules and executables more often. Having lots of entry points can be handy, and makes a lot of sense in many cases (e.g. whenever you have a main module with more than one function, it almost always makes sense to be able to import it to unit test the components). I would also encourage reserving the "__main__.py" file to be "the one true" entrypoint, or at least the primary public facing entrypoint. I find this pattern strikes a nice balance between having lots of entry points (which is good for people familiar with the code base) and having one obvious entrypoint for people learning the codebase. On Sun, Oct 3, 2021 at 10:20 AM David Mertz, Ph.D. <david.mertz@gmail.com> wrote:
On Sun, Oct 3, 2021, 1:46 AM Christopher Barker
The if __name__ block is only required for a Python file to be both a module and a script. That’s actually a pretty uncommon thing— if it’s a module to be imported by other modules, then it probably should be part of a package, and if the functionality needs to be made available as a script, there can be another module just for that.
I think all the slightly different boilerplate forms suggested are needless. So on the main topic, -1.
But as a note, I usually put a __name__=='__main__' block at the foot of my library code.
The few functions in this particular file presumably do something useful, and hopefully composable. Why not allow command-line users to do just that one thing by itself? Sure, the predominant use is within a library, but being able to try out this functionality usually helps my development, and is sometimes useful to have around and composable in bash. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6XGPJH... Code of Conduct: http://python.org/psf/codeofconduct/
-- -Dr. Jon Crall (him)
On Mon, Oct 4, 2021 at 2:02 AM Jonathan Crall <erotemic@gmail.com> wrote:
@Marc-Andre
One of the motivations of this proposal is to incentivize writing code in a local scope, rather than a global one. People in this thread have made the argument "Python is a scripting language, so it should do that". Ok, fine. Yes `print('hello world')` is more idiomatic than the one inside of a function, but any real work done in Python quickly discards that global simplicity.
If it's already true that real work discards the simplicity of working at global scope, then why "incentivize" local scope? Scripts that need to use functions already do. Scripts that don't.... shouldn't be punished for it. ChrisA
On Sun, Oct 3, 2021 at 8:00 AM Jonathan Crall <erotemic@gmail.com> wrote:
One of the motivations of this proposal is to incentivize writing code in a local scope, rather than a global one.
One of the motivations for me posting this was a several students I was working with were putting all of their logic in the `if __name__ == '__main__'` block, defining global variables in them, and then relying on
I'm not sure that's something to be incentivised necessarily ;-) Yes, incentivising good code structure is a good thing, but using global scope for scripts isn't necessarily a bad thing -- an it actually makes debugging scripts a touch eraser -- e.g. I like to use iPython's `run`- and then i have those global names right there, ready to use, those global variable existing in other places in the code. I think the problem there is overuse of the __name__ block -- why have it at all? I suspect these students saw that pattern and thought is was necessary, much like a main() function is necessary in some languages. After all, they had already done the "hard part"of writing that awkward if black, calling a function in it would have been no harder, if they were using functions at all. With my students, I start them aff writing scripts, then work on moving logic into functions, and later on introduce the if __name__ block (normally for simple tests first) @Christopher Barker
The if __name__ block is only required for a Python file to be both a module and a script. That’s actually a pretty uncommon thing—
I suppose I do an uncommon thing fairly often. I work with neural networks and I generally construct a package with a pkg/fit.py pkg/predit.py and pkg/evaluate.py which all operate as both a module and a script. I'll run `python -m pkg.fit [args]` to train a network but I'll also `from pkg import fit`, especially when testing. I think if you are testing an executable module you almost always want to have part of it be importable so it can be unit tested.
absolutely -- but why does you code really fit into that neat structure where all the functions are only needed by a particular script? For the most part, once I've made a package, I'd have a collection of functions, etc, and then each script would import and use a different subset of those code -- but it's not a 1:many relationship, it's many:many So the "real code" wouln'dt be in the same files as the scripts. And may have one script with opttions: ./this_model fit ./this_model evaluate ... Anyway, I do think your example of scripts needing multiple testable functions that are used only by that one script is actually pretty rare.
the simplicity of the case where it makes intuitive sense that the package module should be accessible as a module and a library.
did you mean "script" and a library ? But anyway, that's what if __name__ == "__main__" is for -- no one's saying you shouldn't use it ... Retrospectively, I would agree that this is probably uncommon, but if
nothing else comes out of this discussion, I would want to encourage Python users to write Python files as both modules and executables more often. Having lots of entry points can be handy, and makes a lot of sense in many cases (e.g. whenever you have a main module with more than one function, it almost always makes sense to be able to import it to unit test the components).
Though if it's that coupled, maybe the unit tests should be in the same file as well :-) if you define a few "test_*" functions in aa file, you can pass the whole thing tto pytest to run them ..;. Anyway --- I personally think if __name__ == "__main__" is a bit cryptic, and kins leaks an implementation detail -- after all, we don't usually access dunders directly -- e.g. len(something) rather than something.__len__() -- so I'm pefer something like: if running_as_a_script: do the stuff. But is it worth a change now? the if __name__ == "__main__" idiom has been around a long time -- it will be supported forever, and examples and explanations of it will be around a avery, very long time -- making a new way now will only add a lot of confusion for years, with only a little benefit. -CHB -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
FYI you can already use package/__main__.py which is runnable with `python -m package` and you don't need the `if __name__ == "__main__":` https://docs.python.org/3.10/library/__main__.html#main-py-in-python-package... On Fri, 1 Oct 2021, 20:39 Paul Bryan, <pbryan@anode.ca> wrote:
How about the following?
def __main__():
...
Behavior:
1. Load module as normal. 2. If __name__ is "__main__" or module is named in python -m, call __main__ function.
Paul
On Fri, 2021-10-01 at 15:35 -0400, Jonathan Crall wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/RNB5YY... Code of Conduct: http://python.org/psf/codeofconduct/
For sure. I think there can be room for improvement in modules though. On Fri, 2021-10-01 at 20:43 +0100, Thomas Grainger wrote:
FYI you can already use package/__main__.py which is runnable with `python -m package` and you don't need the `if __name__ == "__main__":` https://docs.python.org/3.10/library/__main__.html#main-py-in-python-package...
On Fri, 1 Oct 2021, 20:39 Paul Bryan, <pbryan@anode.ca> wrote:
How about the following?
def __main__(): ...
Behavior:
1. Load module as normal. 2. If __name__ is "__main__" or module is named in python -m, call __main__ function.
Paul
On Fri, 2021-10-01 at 15:35 -0400, Jonathan Crall wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N...
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/RNB5YY...
Code of Conduct: http://python.org/psf/codeofconduct/
Yes, it seems like having special handling of `def __main__` is an extension in the spirit of `package/__main__.py `. On Fri, Oct 1, 2021 at 3:43 PM Thomas Grainger <tagrain@gmail.com> wrote:
FYI you can already use package/__main__.py which is runnable with `python -m package` and you don't need the `if __name__ == "__main__":` https://docs.python.org/3.10/library/__main__.html#main-py-in-python-package...
On Fri, 1 Oct 2021, 20:39 Paul Bryan, <pbryan@anode.ca> wrote:
How about the following?
def __main__():
...
Behavior:
1. Load module as normal. 2. If __name__ is "__main__" or module is named in python -m, call __main__ function.
Paul
On Fri, 2021-10-01 at 15:35 -0400, Jonathan Crall wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/RNB5YY... Code of Conduct: http://python.org/psf/codeofconduct/
-- -Dr. Jon Crall (him)
See the rejected PEP 299 for a previous version of this idea: https://www.python.org/dev/peps/pep-0299/ And a discussion on python-dev: https://mail.python.org/pipermail/python-dev/2006-March/062951.html Eric On 10/1/2021 3:38 PM, Paul Bryan wrote:
How about the following?
def __main__(): ... Behavior:
1. Load module as normal. 2. If __name__ is "__main__" or module is named in python -m, call __main__ function.
Paul
On Fri, 2021-10-01 at 15:35 -0400, Jonathan Crall wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto:python-ideas-leave@python.org> https://mail.python.org/mailman3/lists/python-ideas.python.org/ <https://mail.python.org/mailman3/lists/python-ideas.python.org/> Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N... <https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2NEI5RQMTX53N77KQQDFZ6HZONXU/> Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/>
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/RNB5YY... Code of Conduct: http://python.org/psf/codeofconduct/
Thanks for finding that. While I don't feel strongly one way or the other, I do think the discussion is worthwhile. As I understand, the arguments for: - let's get rid of boilerplate, that many (esp. beginners) may not understand - you could add command line arguments and return values in a more natural way As I understand, the arguments against: - it's just 2 lines of code; this isn't a big problem being solved - boilerplate serves to teach a fundamental concept about the loading and execution of Python modules - there may not be a clean way to maintain compatibility with previous versions of Python Paul On Sat, 2021-10-02 at 12:21 -0400, Eric V. Smith wrote:
See the rejected PEP 299 for a previous version of this idea: https://www.python.org/dev/peps/pep-0299/
And a discussion on python-dev: https://mail.python.org/pipermail/python-dev/2006-March/062951.html
Eric
On 10/1/2021 3:38 PM, Paul Bryan wrote:
How about the following?
def __main__(): ... Behavior:
1. Load module as normal. 2. If __name__ is "__main__" or module is named in python -m, call __main__ function.
Paul
On Fri, 2021-10-01 at 15:35 -0400, Jonathan Crall wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/RNB5YY... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/3X4X65... Code of Conduct: http://python.org/psf/codeofconduct/
On 2 Oct 2021, at 18:17, Paul Bryan <pbryan@anode.ca> wrote:
Thanks for finding that.
While I don't feel strongly one way or the other, I do think the discussion is worthwhile.
As I understand, the arguments for: - let's get rid of boilerplate, that many (esp. beginners) may not understand - you could add command line arguments and return values in a more natural way
As I understand, the arguments against: - it's just 2 lines of code; this isn't a big problem being solved - boilerplate serves to teach a fundamental concept about the loading and execution of Python modules - there may not be a clean way to maintain compatibility with previous versions of Python
It is 2 lines of code that a beginner does not need to write at all. I see a lot of short scripts that do not bother with the main function at all, they just put all the code at module level. And why not? Indeed I do not bother for short scripts either. Once someone is doing more complex scripting then the benifits of the 2 lines and main function become something worth understanding and using. Namely it allows importing the code and testing it. Barry
On Sun, Oct 3, 2021 at 4:18 AM Paul Bryan <pbryan@anode.ca> wrote:
Thanks for finding that.
While I don't feel strongly one way or the other, I do think the discussion is worthwhile.
As I understand, the arguments for: - let's get rid of boilerplate, that many (esp. beginners) may not understand
It's not really boilerplate. The idiom exists for situations where one block of code needs to be able to do two different things (or needs to do something or not do that something) based on some run-time condition. It is, exactly and perfectly, the job of an if statement.
- you could add command line arguments and return values in a more natural way
As I understand, the arguments against: - it's just 2 lines of code; this isn't a big problem being solved - boilerplate serves to teach a fundamental concept about the loading and execution of Python modules
This is more important than you might think. It's a reminder that, unlike in some languages, top-level Python code is just code. It executes from the top down, and it can do things at the points that it comes to them. This is a much bigger deal than a lot of beginners realise, and I don't see any value in hiding that behind something that encourages an idiom of "write everything in functions, one function is magically called, and then everything happens".
- there may not be a clean way to maintain compatibility with previous versions of Python
The way to maintain compatibility is... don't use the new idiom. If you need your code to run on older Pythons, keep using the if-name-is-main idiom. I don't think the backward compatibility break of giving meaning to a dunder name is particularly significant; dunder names are special, so if you've been writing "if __name__ == '__main__': __main__()", then you're opening yourself up to problems anyway (just call it main() and be done with it). ChrisA
On Sat, Oct 02, 2021 at 10:17:12AM -0700, Paul Bryan wrote:
As I understand, the arguments for: - let's get rid of boilerplate, that many (esp. beginners) may not understand
The proposal doesn't get rid of boilerplate, it just changes it from one form to another. The current idiom uses an explicit if test `if __name__ == "__main__"` which would be replaced by some other boilerplate. Some of the proposals have included: def main(): ... __main__(main) or import sys def main(): ... sys.run_main() or just an implicit magic dunder function: def __main__(): ... Whichever proposal we go with, its still boilerplate. Boilerplate is not necessarily bad. Boilerplate code is predictable, standardized code that can be easily reused with little or no variation. Predictable and standardized is another way of saying *idiomatic*, which is a good thing. Boilerplate is only bad when you have to write a lot of boilerplate to get to the small amount of code that actually solves your problem. Another name for that is scaffolding. Python (unlike some languages) rarely has a problem with excessively verbose scaffolding, and a one or two line idiomatic snippet to run code as a script is hardly excessively verbose. -- Steve
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
@Steven D'Aprano <steve@pearwood.info> Yes my original post is somewhat pithy and has some harsh words for decorators. Like many I have some opinions about code. Don't take them too seriously if they disagree with your experience, but I do hold my opinions for reasons, and I will explain some of them, but I don't want to derail my first python ideas proposal that has gotten some semblance of a positive reception. So I will quickly explain my terse statements in a more professional manner. And I will note again, these are opinions. * Avoid global variables, they make state difficult to reason about. Plenty of opinions have been written about this. * Code runs faster when it's in a function then when it is in a global scope. I don't have the reference on hand, but I can get it if anyone's interested. Python is a scripting language, but it's used heavily in production, It's nice if the efficient way of doing something is natural, intuitive, and easy. It's true that it's just a two-line pattern, and not all simplifications of two line patterns make it into the python standard. But this is an extremely common two line pattern. One that's often written less efficiently than it could be. If you already wrote the if statement for name, a new programmer is not incentivized to write a separate main function and then call it, it would be simpler if there was some construct that just declared this is the main function, and then the code inside the function is executed. For giant single file scripts, that are just protected by an if name equals main, this could be a big performance improvement. Although I imagine in the average case it won't impact that much. But there may be some small benefit. The biggest benefit will be in the clarity of the code, the state will be easier to reason about because there will be less globals. My initial proposal could absolutely be implemented in pure Python, but I do think I like the idea of just having a special dunder main function. I am an experienced user who avoids decorators. They make it difficult to statically parse the code. They are useful, but when I was younger I did fall in the trap of abusing them. And I do think there is something to be said for statically parsable code. Having to guarantee that when you see a function definition, that that function definition won't change or be dynamically rewritten, can be nice. There's value in dynamic code, decorators are a good concept, and Python is better for them, but I do find it fun to take jabs at them from time to time. So yes I was being a bit sarcastic and part of the proposal, but I was serious when I proposed using that form of dunder main as a decorator. However now I do favor Python just checking that there is a dunder main function, and calling it. That would be in the spirit of dunder getattr, which imo is one of the best improvements Python has made. On Fri, Oct 1, 2021, 9:01 PM Steven D'Aprano <steve@pearwood.info> wrote:
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.
``` 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 _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/YSCULW... Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, Oct 2, 2021 at 2:01 PM Jonathan Crall <erotemic@gmail.com> wrote:
@Steven D'Aprano Yes my original post is somewhat pithy and has some harsh words for decorators. Like many I have some opinions about code. Don't take them too seriously if they disagree with your experience, but I do hold my opinions for reasons, and I will explain some of them, but I don't want to derail my first python ideas proposal that has gotten some semblance of a positive reception.
So I will quickly explain my terse statements in a more professional manner. And I will note again, these are opinions.
* Avoid global variables, they make state difficult to reason about. Plenty of opinions have been written about this.
* Code runs faster when it's in a function then when it is in a global scope. I don't have the reference on hand, but I can get it if anyone's interested. Python is a scripting language, but it's used heavily in production, It's nice if the efficient way of doing something is natural, intuitive, and easy.
It's true that it's just a two-line pattern, and not all simplifications of two line patterns make it into the python standard. But this is an extremely common two line pattern. One that's often written less efficiently than it could be. If you already wrote the if statement for name, a new programmer is not incentivized to write a separate main function and then call it, it would be simpler if there was some construct that just declared this is the main function, and then the code inside the function is executed.
But it's ONLY necessary when a single Python file is used as both a script and a module. Bear that in mind. ChrisA
That's effectively every module I write. I don't think that's uncommon or bad practice. On Sat, Oct 2, 2021, 12:05 AM Chris Angelico <rosuav@gmail.com> wrote:
On Sat, Oct 2, 2021 at 2:01 PM Jonathan Crall <erotemic@gmail.com> wrote:
@Steven D'Aprano Yes my original post is somewhat pithy and has some
harsh words for decorators. Like many I have some opinions about code. Don't take them too seriously if they disagree with your experience, but I do hold my opinions for reasons, and I will explain some of them, but I don't want to derail my first python ideas proposal that has gotten some semblance of a positive reception.
So I will quickly explain my terse statements in a more professional
manner. And I will note again, these are opinions.
* Avoid global variables, they make state difficult to reason about.
Plenty of opinions have been written about this.
* Code runs faster when it's in a function then when it is in a global
scope. I don't have the reference on hand, but I can get it if anyone's interested. Python is a scripting language, but it's used heavily in production, It's nice if the efficient way of doing something is natural, intuitive, and easy.
It's true that it's just a two-line pattern, and not all simplifications
of two line patterns make it into the python standard. But this is an extremely common two line pattern. One that's often written less efficiently than it could be. If you already wrote the if statement for name, a new programmer is not incentivized to write a separate main function and then call it, it would be simpler if there was some construct that just declared this is the main function, and then the code inside the function is executed.
But it's ONLY necessary when a single Python file is used as both a script and a module. Bear that in mind.
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/B3RDNS... Code of Conduct: http://python.org/psf/codeofconduct/
def __main__() at the end of the file will be something nice to have. It’s much easier to explain to new students than if __name__ == __main__: main(). Can we import this new __main__ function from another file and use it in another file? Maybe it should be disallowed to remove confusion and conflict or only allowed if it was imported as another name (e.g. from file_b import __main__ as main_b) It would take optional arguments like number of command line arguments and the command line arguments as a list similar to C and C++: def __main__(argc: int, argv: list[str]): … Abdulla Sent from my iPhone
On 2 Oct 2021, at 10:43 AM, Chris Angelico <rosuav@gmail.com> wrote:
On Sat, Oct 2, 2021 at 3:15 PM Jonathan Crall <erotemic@gmail.com> wrote:
That's effectively every module I write. I don't think that's uncommon or bad practice.
Interesting. Why is that?
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6URF34... Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, Oct 02, 2021 at 01:15:28AM -0400, Jonathan Crall wrote:
On Sat, Oct 2, 2021, 12:05 AM Chris Angelico <rosuav@gmail.com> wrote:
But it's ONLY necessary when a single Python file is used as both a script and a module. Bear that in mind.
That's effectively every module I write. I don't think that's uncommon or bad practice.
It's not *bad* practice as such, or at least not necessarily, but it does open you to a particular failure mode that can be avoided by keeping your scripts and importable modules separate. Things are fine when the one .py file is used as either an importable module, or as an executable script, but if you ever have a situation where the same .py file gets used as both *simultaneously*, then you break the invariant that each module is a singleton. The most common way that occurs is, by memory (untested): # spam.py import eggs if __name__ == "__main__": do_something() # eggs.py import spam When you import spam, you just have a regular old circular import. But when you run spam as a script, you end up with two copies of the module, one called 'spam' and one called '__main__'. Whether that ends up being a problem or not depends on the circumstances. So many people have a coding standard to always keep the two (importable modules and scripts) as separate files. -- Steve
On Fri, Oct 01, 2021 at 11:59:33PM -0400, Jonathan Crall wrote:
It's true that it's just a two-line pattern, and not all simplifications of two line patterns make it into the python standard. But this is an extremely common two line pattern. One that's often written less efficiently than it could be. If you already wrote the if statement for name, a new programmer is not incentivized to write a separate main function and then call it, it would be simpler if there was some construct that just declared this is the main function, and then the code inside the function is executed.
For giant single file scripts, that are just protected by an if name equals main, this could be a big performance improvement. Although I imagine in the average case it won't impact that much. But there may be some small benefit. The biggest benefit will be in the clarity of the code, the state will be easier to reason about because there will be less globals.
*This*. For me, the clarity of code is the most important aspect. Instead of open-coding a very common runtime check, we *declare* that some function is the entry point. People have argued elsewhere in the thread that writing 'if __name__ == …' is easy. It is, but it also gets in the way: 1. we don't have a function scope. When the "main block" is a few lines, this doesn't matter so much. But quite often it starts as a line or two, but then you add option parsing, and maybe some prints, and then have non-trivial code executed in the top-level scope, with variables that are *meant* as local visible in the module global scope. (In my experience it's a fairly common bug where we want to pass all state to functions through parameters, but forget a variable. The function still works because it gets the variable from the global scope, but when it's called from a different module, without going through the "main block", the variable is not defined.) 2. If we want to call the "main block" from a different module, either for testing or for any other reason, we can't without jumping through hoops. 3. I know some people argue that 'if __name__ == …' is very pythonic and just great, but in my experience with teaching python, it is confusing and unclear. The problem is that it is pretty much the only place where this kind of conditional would be used. Unless you're doing some advanced stuff, there is simply no other reason to ever check the name of the current module. So explaining 'if __name__ == …' requires explaining what __name__ is, and that it is set one way normally, but to '__main__' in other situations. Overall, 'if __name__ == …' seems like a cute pattern, but it doesn't "scale" properly. Zbyszek
I have also often wondered if there could be something more turn-key for that bit at the bottom. Here is a suggestion I'll throw into the ring: sys.run_main() It goes at the bottom of the file, and basically just lets the programmer express what they want to happen. Easy to explain. The appropriate logic is buried in run_main(), checking if we're in the __main__ situation, and looking for a main() in the file and running it and probably some corner cases I haven't thought of. I figure sys is the right module, having argv and all that. I would prefer to not have the programmer need to resort to underbar __main()__ type naming to accomplish a thing that is so ordinary. If there were a syntax for this, visualize it showing up in the first day of Python class, at the bottom of hello.py or whatever. You want something easy to explain, not requiring a ton of attention for solving something that is actually quite routine. I don't mind making the name 'main' special, but you could have an option to specify a different function or make it more agnostic about the name in some other way. Best, Nick On Fri, Oct 1, 2021 at 12:36 PM Jonathan Crall <erotemic@gmail.com> wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him) _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N... Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, Oct 2, 2021 at 1:00 PM Nick Parlante <nick@cs.stanford.edu> wrote:
I have also often wondered if there could be something more turn-key for that bit at the bottom.
Here is a suggestion I'll throw into the ring:
sys.run_main()
It goes at the bottom of the file, and basically just lets the programmer express what they want to happen. Easy to explain. The appropriate logic is buried in run_main(), checking if we're in the __main__ situation, and looking for a main() in the file and running it and probably some corner cases I haven't thought of. I figure sys is the right module, having argv and all that. I would prefer to not have the programmer need to resort to underbar __main()__ type naming to accomplish a thing that is so ordinary. If there were a syntax for this, visualize it showing up in the first day of Python class, at the bottom of hello.py or whatever. You want something easy to explain, not requiring a ton of attention for solving something that is actually quite routine.
I don't mind making the name 'main' special, but you could have an option to specify a different function or make it more agnostic about the name in some other way.
Here's another suggestion. Instead of magical things that are supposed to do what you expect, just write your code and have it do what it does. If you find that you need it to do different things depending on whether it's run as a script or imported as a module, write a condition that tests this. Otherwise, don't. ChrisA
01.10.21 22:35, Jonathan Crall пише:
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.
It is not an attipattern. It is how Python works (note that import, def and class are statements executable at runtime), and it is what you normally expect from a scripting language. Now, sometimes you want to use the same file for two purposes: as a module and as an executable script. You need a way to distinguish these cases. And __name__ == "__main__" is an idiomatic way to do it in Python. But if you do not need to mix executable and module in one file you do not need to use it.
Greetings list, I am -1 on this proposition. I can relate that if name == main is very confusing to teach. However, it teaches a lot about how Python works. If you know Python it is very clear. So if it's confusing, you wrongly taught Python (I myself was in this situation) The simplification idea is to coerce Python to use patterns forged elsewhere. The tools are here if you wish to use it. But, specifically pointing a pattern for it and adding additional layers to make it work is an enforcement of the main function. Something python is not bound to and does not need to run. Kind Regards, Abdur-Rahmaan Janhangeer about <https://compileralchemy.github.io/> | blog <https://www.pythonkitchen.com> github <https://github.com/Abdur-RahmaanJ> Mauritius
I disagree that "it teaches a lot about how Python works" is a good reason to keep things the way they are. If you applied this principle more broadly, it would seem to be an argument in favour of complexity in most situations, that would imply we should keep syntactic sugar to a bare minimum at all times. Learning about how Python works under the hood is extremely valuable for becoming a more advanced programmer. However, not everybody needs to be a mechanic to drive a car. We should surely make it as easy as possible for beginners to write fully functional scripts that conform to best practices. I love this proposal. "if__name__ == '__main__'" has always felt, to me, as though it flies in the face of the simplicity and elegance Python generally prizes in other parts of the language. Best wishes, Alex
On 2 Oct 2021, at 11:00, Abdur-Rahmaan Janhangeer <arj.python@gmail.com> wrote:
Greetings list,
I am -1 on this proposition.
I can relate that if name == main is very confusing to teach. However, it teaches a lot about how Python works. If you know Python it is very clear. So if it's confusing, you wrongly taught Python (I myself was in this situation)
The simplification idea is to coerce Python to use patterns forged elsewhere. The tools are here if you wish to use it.
But, specifically pointing a pattern for it and adding additional layers to make it work is an enforcement of the main function. Something python is not bound to and does not need to run.
Kind Regards,
Abdur-Rahmaan Janhangeer about | blog github Mauritius _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/VHZK76... Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, Oct 02, 2021 at 05:13:01PM +0100, Alex Waygood wrote:
I disagree that "it teaches a lot about how Python works" is a good reason to keep things the way they are. If you applied this principle more broadly, it would seem to be an argument in favour of complexity in most situations, that would imply we should keep syntactic sugar to a bare minimum at all times.
Not necessarily the bare minimum (which is a subjective judgement), but yes, we should keep the amount of syntactic sugar low. Syntactic sugar that has clear benefits is a win. For example, decorator syntax is syntactic sugar that has been a great win for the language because it makes it easier to use and read a powerful design pattern, the Decorator Pattern. Comprehensions have been a great win for the language because they have allowed us to simplify something which was a minimum of three statements into a single expression. `yield from` is syntactic sugar for something which requires a complicated, nested set of multiple try...except blocks and for loops to do correctly. (See the PEP for details.) These have all been examples of good syntactic sugar. Syntactic sugar without such benefits will just rot your teeth :-) Whereas this is, *at best*, a trivial benefit (save two lines). It doesn't make it easier to do something hard and complicated (like yield from) or avoid violating DRY (like decorator syntax), or do something previously impossible (write an explicit loop as an expression). It doesn't simply your code, or make it easier to write scripts, or do anything that can't already be trivially done, or make your scripts safer. It just replaces one trivial piece of one-line boilerplate: if __name__ == '__main__': with another piece of boilerplate: def __main__(): Yay for progress! We save eleven whole characters, which at current hard drive costs is a saving of approximately 0.00000004 cents :-) (I know, I know, this proposal is not really about saving storage space, but people have mentioned saving typing.) Any way we look at it, this is not going to transform Python programming like decorator syntax, comprehensions or async have. At best all it is going to do is shift one trivial idiom to another trivial idiom, but at a cost of: - obsoleting masses of documentation (tutorials, walk-throughs, videos, demos, example code, blog posts, Stackoverflow answers etc) that recommend the `if __name__ ...` idiom; - add a *long* transition period (maybe forever!) where people, especially newbies, are unsure which idiom is better and whether to use the old or new version; - yet another thing that has to be programmed into the interpreter, tests written and run, and documented. You might argue that the costs are trivially small, but the benefits are correspondingly even smaller.
Learning about how Python works under the hood is extremely valuable for becoming a more advanced programmer. However, not everybody needs to be a mechanic to drive a car. We should surely make it as easy as possible for beginners to write fully functional scripts that conform to best practices.
Stuffing everything in a main() function just for the sake of having it in a main() function is not "best practices", it is cargo-cult programming. Right now, the best practice Hello World program in Python is: print("Hello world!") which is as simple and newbie-friendly as it is possible to get. This alleged "best practice" is not: def main(): print("Hello world!") __main__(main) or any of the other variants involving decorators, special builtins, sys.run_main(), special dunder names, magical implicit behaviour etc.
I love this proposal. "if__name__ == '__main__'" has always felt, to me, as though it flies in the face of the simplicity and elegance Python generally prizes in other parts of the language.
I can't imagine why you think that. Let's run through the Zen: **Beautiful is better than ugly.** That's subjective, and some people think dunders are ugly. Maybe so, but they are ubiquitous in Python. **Explicit is better than implicit.** The current idiom is explicit. **Simple is better than complex.** And simple. **Readability counts.** And readable. **Special cases aren't special enough to break the rules.** The special case of a script main function isn't special enough to be treated as a special case. **There should be one-- and preferably only one --obvious way to do it.** For 30 years, that one obvious way has been `if __name__ == ...`. **If the implementation is easy to explain, it may be a good idea.** The implementation is certainly easy. It's just an `if`, like every other if in the language. The trickiest part is that the interpreter has to set the global variable `__name__` to a string, and that's not even a little bit tricky. So out of the 19 koans in the Zen, six support the current idiom, one is *just maybe* negative, and the rest are neutral.
Greetings list,
I disagree that "it teaches a lot about how Python works" is a good reason to keep things the way they are. If you applied this principle more broadly, it would seem to be an argument in favour of complexity in most situations, that would imply we should keep syntactic sugar to a bare minimum at all times.
Well, when giving superpowers like the decorator pattern, this is a case for it. My case is not against syntactic sugar. My main problem is: "The simplification idea is to coerce Python to use patterns forged elsewhere." Like does Python need to provide official support for a main function? It's best put into Steven's words:
I don't think it is a good idea to give programmers coming from other languages the false impression that Python behaves the same as the language they are familiar with, when it doesn't. The entrypoint to Python scripts is not main(), it is the top of the module.
eidt: forgot to reply all Kind Regards, Abdur-Rahmaan Janhangeer about <https://compileralchemy.github.io/> | blog <https://www.pythonkitchen.com> github <https://github.com/Abdur-RahmaanJ> Mauritius
why not evaluate the condition directly in the decorator, python 3.9 allows that ;-p @lambda x: __name__ == "__main__" and x() def main(): ... On 01/10/2021 21:35, Jonathan Crall wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him)
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N... Code of Conduct: http://python.org/psf/codeofconduct/
I've found that the `if __name__ == "__main__"` idiom is unintuitive and feels unnecessary, and I'd like a cleaner way to write it. However, I don't like the idea of functions being "magically called." One of the things I don't like about C/C++ and so many other languages is the presence of a `main()` function that's implicitly called when you run the script. I don't think functions should be run without being called in the code. (TBH, I never did get far learning C++, so I'm no authority here.) I never liked how you can't import your package in its own __main__.py file (or maybe I'm missing something, packaging isn't my strong suit), so at first I thought, "Here's a way to move the script code into __init__.py!" `python -m package` could just call the main function in __init__.py! Everything could be so simple! However, after reading others' responses to the idea, I'm convinced that this wouldn't be good (i.e. there could be two copies of the module in memory). Keep __main__.py it is. Backwards compatibility isn't an issue. If your code needs to be compatible with previous versions of Python, don't use it. Problem solved. One compelling idea for me is the idea of automatic basic argument parsing. The main function could have command line arguments as arguments to it (like how `main` in other languages takes `argv` as an argument, but we could be specific here, by saying something like `def main(command, verbose=False)`), and the function that calls this main function purses arguments and passes them to the main function. We could use type hints and introspection to make this even more powerful. However, it could get very messy very quickly with options. I do think that this would be good motivation to do this, though. After all, using argparse can only be so much fun. My proposal is to make it look something like: run(main) Where `run` (a placeholder name for the proposed function) only executes if name equals main, and may do argument parsing and pass the arguments to `main`. -- Finn M. On Sat, Oct 2, 2021, 3:19 PM Anthony Baire <anthony.baire@irisa.fr> wrote:
why not evaluate the condition directly in the decorator, python 3.9 allows that ;-p
@lambda x: __name__ == "__main__" and x() def main(): ...
On 01/10/2021 21:35, Jonathan Crall wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him)
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.orghttps://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/4Y6JQJ... Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, Oct 02, 2021 at 08:33:59PM -0600, Finn Mason wrote:
I've found that the `if __name__ == "__main__"` idiom is unintuitive and feels unnecessary,
The Python community thinks nothing of writing code with metaclasses, functions and classes as first-class citizens, recursion, parallisation using threads and process, concurrency with asynchronous coroutines, implementing astonishingly clever algorithms that rely on deep mathematical properties that literally require a decade or more of study to understand, and all that is fine, but checking whether a global variable is set to a particular value is too unintuitive? Of course nobody is going to *intuit* that the global variable is called `__name__` or that the value is `"__main__"`. But neither is anybody going to intuit that the way we load a single function from a module is from module import function as opposed to, let's say, "laden functie van onderdeel". Or that stdout is in the sys module. Or that floating point maths violates associativity: (0.17 + 0.69) + 0.97 != 0.17 + (0.69 + 0.97) These are all things we have to learn, not intuit. The first time I saw Python code, after being told that it was "really intuitive", I couldn't make head or tails of what on earth it was about, especially all those curly braces and square brackets and colons. And what's self, or a class? To me, a class was what you were in when you went to school, or maybe a socio-economic group. If Python was so intuitive, maybe it was just me, I was an idiot for not being able to guess the meaning from first principles. So I think that we sometimes take "unintuitive" too seriously.
I never liked how you can't import your package in its own __main__.py file (or maybe I'm missing something, packaging isn't my strong suit)
I would expect that either a relative import: from . import function or an absolute import: from mypackage import function import mypackage should work. -- Steve
Maybe @lambda x: x if __name__ == "__main__" and x() else x so you can call the main function again, if you want. On Sat, Oct 2, 2021 at 6:19 PM Anthony Baire <anthony.baire@irisa.fr> wrote:
why not evaluate the condition directly in the decorator, python 3.9 allows that ;-p
@lambda x: __name__ == "__main__" and x() def main(): ...
On 01/10/2021 21:35, Jonathan Crall wrote:
I was curious if / what sort of proposals have been considered for simplifying the pattern:
``` def main(): ...
if __name__ == "__main__": main() ```
I imagine this topic must have come up before, so I'd be interested in any relevant history.
But unless I'm missing something, it seems like adding some easier alternative to this cumbersome entrypoint syntax would be worth considering.
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. 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) ```
In addition to being simpler, it would allow users to avoid the trap of adding logic that impacts the global scope. It would also save me some keystrokes, which I'm always grateful for.
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.
``` @__main__ def main(): ... ```
Maybe having such a builtin would discourage globals and help new users get the use-decorators-everywhere bug out of their system.
-- -Dr. Jon Crall (him)
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.orghttps://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FKQS2N... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/4Y6JQJ... Code of Conduct: http://python.org/psf/codeofconduct/
Greetings list, Just a funny Reddit quote: (.NET 5 minimal APIs are even easier, and I swear .NET starts to feel more like Python to me all the time, especially since they removed the need for a Main method). https://www.reddit.com/r/flask/comments/q0u6ci/scaling_flask_apis_for_highth... Kind Regards, Abdur-Rahmaan Janhangeer about <https://compileralchemy.github.io/> | blog <https://www.pythonkitchen.com> github <https://github.com/Abdur-RahmaanJ> Mauritius
participants (19)
-
Abdulla Al Kathiri
-
Abdur-Rahmaan Janhangeer
-
Alex Waygood
-
Anthony Baire
-
Barry Scott
-
Chris Angelico
-
Christopher Barker
-
David Mertz, Ph.D.
-
Eric V. Smith
-
Finn Mason
-
Jonathan Crall
-
João Bernardo
-
Marc-Andre Lemburg
-
Nick Parlante
-
Paul Bryan
-
Serhiy Storchaka
-
Steven D'Aprano
-
Thomas Grainger
-
Zbigniew Jędrzejewski-Szmek