Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is __main__.py?

Tags:

python

What is the __main__.py file for, what sort of code should I put into it, and when should I have one?

like image 991
Monika Sulik Avatar asked Oct 28 '10 12:10

Monika Sulik


2 Answers

Often, a Python program is run by naming a .py file on the command line:

$ python my_program.py 

You can also create a directory or zipfile full of code, and include a __main__.py. Then you can simply name the directory or zipfile on the command line, and it executes the __main__.py automatically:

$ python my_program_dir $ python my_program.zip # Or, if the program is accessible as a module $ python -m my_program 

You'll have to decide for yourself whether your application could benefit from being executed like this.


Note that a __main__ module usually doesn't come from a __main__.py file. It can, but it usually doesn't. When you run a script like python my_program.py, the script will run as the __main__ module instead of the my_program module. This also happens for modules run as python -m my_module, or in several other ways.

If you saw the name __main__ in an error message, that doesn't necessarily mean you should be looking for a __main__.py file.

like image 104
Ned Batchelder Avatar answered Oct 22 '22 16:10

Ned Batchelder


What is the __main__.py file for?

When creating a Python module, it is common to make the module execute some functionality (usually contained in a main function) when run as the entry point of the program. This is typically done with the following common idiom placed at the bottom of most Python files:

if __name__ == '__main__':     # execute only if run as the entry point into the program     main() 

You can get the same semantics for a Python package with __main__.py, which might have the following structure:

. └── demo     ├── __init__.py     └── __main__.py 

To see this, paste the below into a Python 3 shell:

from pathlib import Path  demo = Path.cwd() / 'demo' demo.mkdir()  (demo / '__init__.py').write_text(""" print('demo/__init__.py executed')  def main():     print('main() executed') """)  (demo / '__main__.py').write_text(""" print('demo/__main__.py executed')  from demo import main  main() """) 

We can treat demo as a package and actually import it, which executes the top-level code in the __init__.py (but not the main function):

>>> import demo demo/__init__.py executed 

When we use the package as the entry point to the program, we perform the code in the __main__.py, which imports the __init__.py first:

$ python -m demo demo/__init__.py executed demo/__main__.py executed main() executed 

You can derive this from the documentation. The documentation says:

__main__ — Top-level script environment

'__main__' is the name of the scope in which top-level code executes. A module’s __name__ is set equal to '__main__' when read from standard input, a script, or from an interactive prompt.

A module can discover whether or not it is running in the main scope by checking its own __name__, which allows a common idiom for conditionally executing code in a module when it is run as a script or with python -m but not when it is imported:

if __name__ == '__main__':      # execute only if run as a script      main() 

For a package, the same effect can be achieved by including a __main__.py module, the contents of which will be executed when the module is run with -m.

Zipped

You can also zip up this directory, including the __main__.py, into a single file and run it from the command line like this - but note that zipped packages can't execute sub-packages or submodules as the entry point:

from pathlib import Path  demo = Path.cwd() / 'demo2' demo.mkdir()  (demo / '__init__.py').write_text(""" print('demo2/__init__.py executed')  def main():     print('main() executed') """)  (demo / '__main__.py').write_text(""" print('demo2/__main__.py executed')  from __init__ import main  main() """) 

Note the subtle change - we are importing main from __init__ instead of demo2 - this zipped directory is not being treated as a package, but as a directory of scripts. So it must be used without the -m flag.

Particularly relevant to the question - zipapp causes the zipped directory to execute the __main__.py by default - and it is executed first, before __init__.py:

$ python -m zipapp demo2 -o demo2zip $ python demo2zip demo2/__main__.py executed demo2/__init__.py executed main() executed 

Note again, this zipped directory is not a package - you cannot import it either.

like image 31
Russia Must Remove Putin Avatar answered Oct 22 '22 17:10

Russia Must Remove Putin