By Pankaj Kumar and Vinayak Baranwal

The Python main function is the conventional entry point for a script: you define a function (typically named main) that holds your program logic and call it only when the file is run directly, not when it is imported as a module. Python does not call main() automatically; the pattern if __name__ == "__main__": is what lets you separate “run as script” from “import as module.” When the file is executed directly, the interpreter sets the special __name__ variable to "__main__", so the block under that condition runs and can call main(). When the file is imported, __name__ is set to the module name, so that block is skipped and your module can be reused without side effects. This article shows how to define and use the Python main function, how CPython assigns __name__, how to make packages runnable with __main__.py, how to integrate argparse, and how to structure scripts for testability and correct exit codes.
if __name__ == "__main__": block so it runs only when the file is executed directly.__name__ to "__main__" when you run a file with python file.py, and to the module name (filename without .py) when the file is imported, which is how the guard works.def main() function to hold your script logic and call it from the if __name__ == "__main__": block; use sys.exit(main()) so the process returns a proper exit code to the shell.__main__.py in the package directory so python -m packagename runs that file as the Python entry point.if __name__ == "__main__": guard keeps top-level code from running on import, which makes scripts testable and avoids side effects when the file is used as a module.In Python there is no built-in “main” that the interpreter runs. The Python main function is a convention: you define a function (usually named main) that contains your script’s logic and invoke it only when the file is run as the main program. This gives you a clear Python entry point and keeps the file usable as a module.
Without a guard, every top-level statement in a file runs both when you execute the file and when you import it. That leads to unwanted side effects when the file is used as a module. The standard approach is to put the call to your main function inside if __name__ == "__main__":, so the Python script execution flow runs your entry point only when the file is executed directly, not when it is imported.
The following example shows the pattern. Top-level print runs in both cases; the call to main() runs only when the file is executed directly.
print("Module loading: top-level code runs on import and on direct run")
def main():
print("python main function: entry point when run directly")
if __name__ == "__main__":
main()
When you run this file directly:
Module loading: top-level code runs on import and on direct run
python main function: entry point when run directly
When you import it from another script, you will see only the first line; the block under if __name__ == "__main__": does not run, so the difference between Python module vs script execution is clear.
The __name__ global variable Python sets for every module is the key to the main-guard pattern. It is a string that identifies how the current file is being used: as the main program or as an imported module.
When CPython runs a file, it sets __name__ in a few common ways:
python myfile.py. CPython sets __name__ to the string "__main__" for that file. So the file is treated as the Python main module.import myfile or import pkg.myfile). CPython loads the module and sets __name__ to its import name: "myfile" in the first case and "pkg.myfile" in the second.-m: You run python -m some_module (or python -m pkg.myfile). Python finds that module and executes it as a script, setting __name__ to "__main__" inside that module.So the same file can behave as either the main program or a library depending on Python import vs direct execution. The following example demonstrates both paths with annotated output.
File: greet.py
print("greet.py is executing. __name__ =", repr(__name__))
def main():
print("Hello from main()")
if __name__ == "__main__":
main()
print("(Direct run: __name__ was __main__, so this block ran)")
else:
print("(Imported: __name__ was", repr(__name__) + ", so main() was not called)")
Run directly: python greet.py
greet.py is executing. __name__ = '__main__'
Hello from main()
(Direct run: __name__ was __main__, so this block ran)
Import from another script: python -c "import greet"
greet.py is executing. __name__ = 'greet'
(Imported: __name__ was 'greet', so main() was not called)
So: when run directly, __name__ is "__main__" and main() runs; when imported, __name__ is the module name and the main block is skipped. That is the internal mechanism behind the Python main function pattern. Python interpreter execution order is always top to bottom; the only difference is the value of __name__ when your code runs.
The condition if __name__ == "__main__": is the standard way to mark the block that should run only when the file is the main program. It implements the Python reusable script pattern: the same file can be both a script and an importable module.
Anything inside that block is Python top-level code that runs only on direct execution. Typically you call a main() function there so all script logic lives in one place. If you omit the guard, every top-level statement runs on import too, which can cause side effects (prints, network calls, file writes) when the file is used as a module.
The next section ties this to testability: the guard is what lets test runners import your script without running its entry point.
You can name your entry-point function anything, but the convention is main. Defining a def main() Python function and calling it from the if __name__ == "__main__": block keeps script logic in one place and makes the entry point obvious.
Define main() before the guard so it exists when the block runs. If the guard appears above the function definition, you will get NameError: name 'main' is not defined when the file is run directly.
Example:
import sys
def main():
print("Running main logic")
return 0
if __name__ == "__main__":
sys.exit(main())
Running the file directly produces:
Running main logic
Using a single main() function is the standard Python best practices script structure for small scripts and CLI tools.
A Python package (a directory containing an __init__.py) can be run as a script by adding a __main__.py file inside that directory. When you run python -m packagename, the interpreter looks for packagename/__main__.py and executes it as the Python entry point. This is how many CLI tools packaged as directories are invoked (e.g. python -m pip, python -m http.server).
Minimal layout:
mytool/
__init__.py
__main__.py
mytool/__init__.py (can be empty):
# Package marker; can be empty
mytool/__main__.py:
import sys
def main():
print("Running mytool as a package")
return 0
sys.exit(main())
Note: __main__.py is executed directly by the interpreter when you run python -m mytool, so __name__ is always "__main__" in that file. You do not need an if __name__ == "__main__": guard here unless you also intend to import __main__.py as a module, which is uncommon.
From the directory that contains mytool, run:
python -m mytool
Running mytool as a package
Python invokes mytool/__main__.py when you use python -m mytool.
Note: The parent directory containing mytool must be on sys.path (e.g. run from that directory or install the package). For more on package layout, see How to Make a Python Package.
You can wire argparse into your Python main function by defining the parser and parsing in the if __name__ == "__main__": block, then passing the parsed namespace into main(). That keeps argument parsing at the entry point and leaves main() testable with plain values.
Example: argument definition, parsing, and passing parsed args through main().
import argparse
import sys
def main(args):
greeting = f"Hello, {args.name}!"
if args.loud:
greeting = greeting.upper()
print(greeting)
return 0
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Echo a greeting")
parser.add_argument("--name", default="World", help="Name to greet")
parser.add_argument("--loud", action="store_true", help="Uppercase the greeting")
parsed = parser.parse_args()
sys.exit(main(parsed))
Help output: python script.py --help
usage: script.py [-h] [--name NAME] [--loud]
Echo a greeting
options:
-h, --help show this help message and exit
--name NAME Name to greet (default: World)
--loud Uppercase the greeting
Sample run: python script.py --name "DigitalOcean" --loud
HELLO, DIGITALOCEAN!
For more patterns, see How to Use argparse to Write Command-Line Programs in Python. For defining the functions that use those arguments, see How to Define Functions in Python 3.
The if __name__ == "__main__": guard prevents import-time side effects. When a test suite (e.g. pytest or unittest) imports your script to test its functions, only the module-level code outside the guard runs; the main block does not. So you avoid running the whole program just because you imported it.
Without the guard: script runs its logic on import, which is bad for tests.
# script_without_guard.py
def main():
print("Doing work")
# BAD: runs on import
main()
When a test file does import script_without_guard, it will see “Doing work” printed and any side effects of main() will run. That makes tests noisy and can change global state.
With the guard: script imports cleanly; entry point runs only when executed.
# script_with_guard.py
def main():
print("Doing work")
if __name__ == "__main__":
main()
When a test file does import script_with_guard, only the function definition is loaded; nothing is printed and no side effects run. Tests can call script_with_guard.main() when they need to. So the guard is what makes the script both runnable and safely importable for testing.
Processes report success or failure to the shell via exit codes: 0 usually means success, non-zero means an error. In Python, the process exit code is set by sys.exit(code). If you do not call it, the interpreter exits with 0 when main() returns or when the script finishes, even when your logic detected an error. Using sys.exit(main()) ties the process exit code to the return value of your Python main function.
Have main() return 0 on success and a non-zero integer on failure, then pass that to sys.exit():
import sys
def main():
# Simulate a failure check
if len(sys.argv) > 1 and sys.argv[1] == "--fail":
return 1
print("Success")
return 0
if __name__ == "__main__":
sys.exit(main())
When run without --fail, the script prints “Success” and exits with code 0. When run with --fail, it returns 1 and prints nothing. The shell sees that code (e.g. echo $? prints 0 or 1 after the script runs).
python script.py
Success
echo $?
0
python script.py --fail
echo $?
1
Warning: If main() does not return an integer, sys.exit(main()) may not behave as intended. sys.exit() expects an integer or an object with a compatible meaning; for clarity, have main() explicitly return 0 or a non-zero int.
So for any script that can fail, the sys.exit(main()) pattern is the right approach.
Use the guard for the entry point. Always put the call to your main logic inside if __name__ == "__main__": so the file can be imported without running that logic. This is the standard Python reusable script pattern.
Name the entry-point function main(). You can use another name, but def main() Python convention makes the entry point obvious to other developers and to tools.
Return an exit code from main() and use sys.exit(main()). Have main() return 0 on success and a non-zero integer on failure, and call sys.exit(main()) in the guard block so the process reports the correct exit code to the shell.
Keep argument parsing at the boundary. Parse argparse (or other CLI args) in the if __name__ == "__main__": block or at the start of main(), and pass parsed values into the rest of your code. That keeps main() testable with plain arguments.
Avoid side effects at top level. Put only imports, constants, and function/class definitions at module level. Do not run heavy logic or I/O at top level; do it inside main() or behind the guard so imports stay fast and side-effect free.
For runnable packages, use __main__.py. If your project is a package, add __main__.py and implement the entry point there so users can run it with python -m packagename.
Following these rules keeps your Python script execution flow clear, makes scripts testable and reusable as modules, and aligns with common Python best practices for script structure.
It means: run the following block only when this file is executed as the main program, not when it is imported as a module. When you run the file with python file.py, Python sets __name__ to "__main__", so the condition is true and the block runs. When the file is imported, __name__ is the module name, so the condition is false and the block is skipped.
The main() function is the conventional place for your script’s logic. Python does not call it automatically; you define it and call it from the if __name__ == "__main__": block. It gives you a single, clear entry point and keeps the file importable without running that logic.
It is not required by the language, but it is the standard way to separate “run as script” from “import as module.” Without it, every top-level statement runs on import too, which causes side effects and makes the file hard to test and reuse. For any file that is both run directly and imported, the guard is the recommended pattern.
Python has no built-in main. The equivalent is to define your own function (usually main) and call it from if __name__ == "__main__":. That block is the Python entry point when the file is run directly.
__name__ is a special string set by the interpreter for every module. When a file is run directly with python file.py, __name__ is "__main__". When the file is imported, __name__ is the module name (the filename without .py). The __name__ global variable Python uses is what makes the main-guard pattern work.
__main__.py is used to make a package runnable with python -m packagename. It lives inside the package directory. When you run python -m packagename, the interpreter executes packagename/__main__.py as the entry point. Many CLI tools use this so the package can be invoked as a script.
Parse arguments with argparse (or another parser) at the entry point, then pass the parsed result into main(). For example: in the if __name__ == "__main__": block (or at the start of main()), call parser.parse_args() to get a namespace, then pass that namespace or its attributes into main(args) or use them inside main(). That keeps parsing at the boundary and leaves main() testable with plain values.
So the process reports the correct exit code to the shell (0 for success, non-zero for failure). Have main() return that code and call sys.exit(main()) in the guard block. If you do not call sys.exit(), the process may always exit with 0 even when your logic failed, which breaks scripts and automation that rely on exit codes.
The Python main function pattern—using if __name__ == "__main__": and placing the main logic in a main() function—is a best practice for writing clean, reusable, and testable Python scripts. This approach allows your code to behave correctly whether it is run as a script or imported as a module, avoids top-level side effects, and enables powerful features like __main__.py for packages. By following these conventions and structuring your scripts thoughtfully, you make your Python projects easier to maintain, extend, and incorporate into larger applications or workflows.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
Java and Python Developer for 20+ years, Open Source Enthusiast, Founder of https://www.askpython.com/, https://www.linuxfordevices.com/, and JournalDev.com (acquired by DigitalOcean). Passionate about writing technical articles and sharing knowledge with others. Love Java, Python, Unix and related technologies. Follow my X @PankajWebDev
Building future-ready infrastructure with Linux, Cloud, and DevOps. Full Stack Developer & System Administrator. Technical Writer @ DigitalOcean | GitHub Contributor | Passionate about Docker, PostgreSQL, and Open Source | Exploring NLP & AI-TensorFlow | Nailed over 50+ deployments across production environments.
The best site with the best information I’ve ever found! Thousands of thanks!
- Sensation Person
Well Done Pankaj, very well explained. So far this is the best one from desi !!!
- Srinivas
Still relevant. Thanks for the simple but elegant breakdown. I needed that and truly appreciate it.
- Eric Webb
Thanks so much for this breakdown. It was super clear and helpful for a begginer like myself.
- Nahuel Tamasso
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Full documentation for every DigitalOcean product.
The Wave has everything you need to know about building a business, from raising funding to marketing your product.
Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.
New accounts only. By submitting your email you agree to our Privacy Policy
Scale up as you grow — whether you're running one virtual machine or ten thousand.
Sign up and get $200 in credit for your first 60 days with DigitalOcean.*
*This promotional offer applies to new accounts only.