Understanding Python's -m Flag | Generated by AI
Question: How does the -m flag work in Python?
Answer:
What -m does
python -m <module> tells Python to locate and execute a module as a script, rather than running a file directly.
The mechanics
When you run:
python -m black myfile.py
Python does this:
- Search for the module in
sys.path(PYTHONPATH + site-packages + stdlib) - Find
black/__main__.pyorblack.pyin the package - Execute it as
__main__(set__name__ = "__main__") - Pass remaining args (
myfile.py) assys.argv[1:]
How Python finds modules
# Python searches these locations in order:
python -c "import sys; print('\n'.join(sys.path))"
Output on typical system:
/current/working/directory
/usr/lib/python3.11
/usr/lib/python3.11/lib-dynload
/home/user/.local/lib/python3.11/site-packages
/usr/local/lib/python3.11/dist-packages
How -m differs from direct execution
# ❌ Direct execution (looks for file on disk)
python black.py myfile.py
# Error: No such file or directory (unless black.py is in current dir)
# ✅ Module execution (searches sys.path)
python -m black myfile.py
# Works! Finds black in site-packages/black/__main__.py
Inside a package: __main__.py
For a package to be executable with -m, it needs this structure:
black/
├── __init__.py
├── __main__.py # ← This gets executed
├── core.py
└── ...
__main__.py:
import sys
from black import main
if __name__ == "__main__":
sys.exit(main())
When you run python -m black, Python:
- Imports the
blackpackage - Finds
black/__main__.py - Executes it with
__name__ == "__main__"
Single-file modules
For a single .py file module (not a package):
python -m json.tool input.json # Executes /path/to/json/tool.py
Python finds json/tool.py in stdlib and runs it.
Why -m is useful
# ❌ Hard to remember: where is pip actually installed?
C:\path\to\Scripts\pip.exe install requests
# ✅ Always works: Python knows where pip is
python -m pip install requests
# ❌ Brittle: depends on your PATH
pytest tests/
# ✅ Reliable: uses installed pytest in current environment
python -m pytest tests/
Practical example: build your own CLI
Create a package:
myapp/
├── __init__.py
├── __main__.py
└── cli.py
__main__.py:
import sys
from myapp.cli import main
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
cli.py:
def main(args):
print(f"Running with args: {args}")
return 0
Now run it:
python -m myapp arg1 arg2
# Output: Running with args: ['arg1', 'arg2']
Under the hood (simplified)
# This is roughly what python -m does internally:
import sys
import runpy
module_name = "black"
sys.argv = ["black", "myfile.py"] # Set sys.argv
# Find and execute the module
runpy.run_module(module_name, run_name="__main__", alter_sys=True)
The key is alter_sys=True — it modifies sys.argv and sys.modules so the module thinks it’s the main entry point.
TL;DR: -m searches sys.path for a module, then executes __main__.py (or the module itself) with __name__ == "__main__". It’s portable, reliable, and doesn’t depend on PATH or file locations.