Python Tutorial / Organize Packages and Define a Safe Entry Point

A package groups related modules in a hierarchy. Keeping application execution inside main() and an explicit entry point lets other code import the module without automatically running the program.

Create a regular package structure

This example uses a regular package with __init__.py. Namespace packages can omit that file, but an explicit regular package keeps the introductory structure clear. Run commands from project, the parent of expense_app, rather than from inside the package.

project/
  expense_app/
    __init__.py
    math_tools.py
    __main__.py

For the relevant APIs and platform requirements, see Organize Packages and Define a Safe Entry Point documentation.

Save all files and execute the package

Contents of expense_app/__init__.py:

"""Expense calculation package."""

Contents of expense_app/math_tools.py:

def total(amounts):
    return sum(amounts)

Contents of expense_app/__main__.py:

from .math_tools import total

def main():
    print(total([3500, 8000, 2500]))

if __name__ == "__main__":
    main()

From project, execute:

python -m expense_app

The result is 14000. -m finds the target through module lookup and provides package context. Directly executing python expense_app/__main__.py is different: the relative import needs the package context supplied by module execution.

Use __name__ to control side effects

An execution target receives the name __main__. An imported module receives its package/module name instead. The guard uses that distinction to call main only for direct execution. Function definitions can remain at module level while calculations and file changes wait until the functions are called.

Finding a data file and finding a module follow different rules. Relative data paths depend on the working directory. A distributable program may need explicit path arguments or package-resource facilities instead of assuming that it always starts in its source folder.

Verify import behavior separately

python -c "import expense_app.__main__; print('imported')"

This should print only imported. If it also prints a total, inspect top-level code that is still running during import. Testing application execution and safe reuse separately catches a class of mistakes that an output-only check misses.

Renaming a package requires updating its import paths and execution commands. Publishing a distribution adds pyproject.toml, builds and installation verification; it is a separate task from arranging source folders. The packaging lesson covers those additional steps.