[Python-ideas] Replacing the if __name__ == "__main__" idiom (was Re: making a module callable)

Steven D'Aprano steve at pearwood.info
Mon Nov 25 15:12:21 CET 2013


On Mon, Nov 25, 2013 at 08:20:23PM +1000, Nick Coghlan wrote:

> So, rather than the low level "is_main", perhaps a higher level
> builtin would be appropriate?
> 
> >>> def run_if_main(f):
> ...     import sys
> ...     if sys._getframe(-1).f_globals.get("__name__") == "__main__":
> ...         sys.exit(f(sys.argv))
> ...
> 
> Starting to get a little too magical for my taste at that point,
> although with a small tweak (to return "f" in the "not main" case) it
> *does* allow the idiom:
> 
>     @run_if_main
>     def main(argv):
>         ...


I love decorators, I really do, but I strongly feel that a decorator is 
completely the wrong API for this functionality. I can't quite put it 
into words, it's an aesthetic thing, but there is one concrete objection 
I can give.

To understand the current "if __name__" idiom, the user only needs to 
understand the if statement and the rule that Python defines a special
global variable __name__. To understand this run_if_main idiom, the user 
has to understand decorators. It's hard to believe, but I've come across 
people -- even quite experienced Python developers -- who avoid 
decorators because they don't quite get how they work. And of course 
beginners have no idea what a decorator is. These means explaining about 
higher-order functions.

For something as basic as running a main function when called as a 
script, one shouldn't have to understand functional programming, higher 
order functions and decorators.

Here are the alternatives, as I see them:


(1) Keep the status quo. 

I don't actually dislike the status quo, even though 
after 15 years I still write "if __name__ is '__main__'" and have to go 
back and correct it :-)

This is a bit magical, so although it's a perfectly acceptable solution, 
perhaps we can make something a bit less magical? (Or at least, stick 
the magic in the implementation, rather than in the user's code.) This 
has suited Python well for 20-odd years, and there's nothing really 
wrong with it.

+0.5 on this.


(2) Have a special function, called "main" or more likely "__main__", 
which is automagically called by Python if and only if the module is 
being run as the main module.

I still have a soft-spot for this, but I'm now satisfied that it is the 
wrong solution. Arguments against:

- Explicit is better than implicit.

- Because it needs support from the compiler, you can't back-port 
  this to older versions of Python.

- People will be confused by the fact that  __main__ is sometimes 
  automatically called and sometimes not.

-1 on this one.


(3) Add an is_main() function to simplify the idiom to:

if is_main():
    ...

Pros: 

- the magic of deciding whether we're running in the main module 
  is hidden behind an abstraction layer;

- even more easily understood than the current "if __name__" idiom;

- easily backported.

Cons:

- one more built-in.

I give this one a +1.


(4) Like #3 above, but make it a (read-only?) global variable, like
    __debug__. Possibly spelled "__main__". The idiom becomes:


if is_main:
    ...


if __main__:
    ...


Pros:

- Some people might feel that "this is the main module" feels 
  more like a global variable than a function call.

Cons:

- If read-only, that requires some magic behind the scenes.

- If not read-only, then people will mess about with it and 
  get confused.

+0.5 if read-only, -0.5 if not.


(5) A decorator-based solution.

Pros:

- More explicit than automagically calling a function based 
  on its name.

Cons:

- Feels wrong to me. I realise that's entirely subjective.

- To me, "run the main function" seems far too basic an 
  operation to justify requiring the user learn about 
  decorators first.

- Have to import the decorator first.

- Unless it's a built-in, in which case, yet another built-in.

- If we go with this solution, the bike-shedding. Oh the bike-
  shedding!

  * what should it pass to the decorated function?

    ~  sys.argv
    ~  a copy of sys.argv
    ~  sys.argv[1:]
    ~  nothing at all

  * should you be allowed to decorate more than one function?

  * should the function(s) be called immediately, or queued up
    and then run after the module is fully loaded?

  * call sys.exit?

I'm -1 on this. There are too many slightly different flavours of this 
solution, and none of them are really hard to solve once you know 
whether or not your in the main module.



-- 
Steven


More information about the Python-ideas mailing list