Introduction
To find where a Python module's source code is located on disk, use module.__file__, inspect.getfile(module), or run python -m site to see all module search paths. This is useful for debugging import issues, reading library source code, finding which version of a module is actually imported, and locating configuration files installed alongside packages.
Method 1: module.file
The __file__ attribute contains the path to the module's source file:
1import os
2print(os.__file__)
3# /usr/lib/python3.11/os.py
4
5import json
6print(json.__file__)
7# /usr/lib/python3.11/json/__init__.py
8
9import requests
10print(requests.__file__)
11# /home/user/.local/lib/python3.11/site-packages/requests/__init__.py
For packages (directories with __init__.py), __file__ points to __init__.py. The parent directory is the package root.
Handling Missing file
Some built-in modules (written in C) do not have __file__:
1import sys
2print(hasattr(sys, '__file__')) # False — sys is a built-in C module
3
4import math
5print(hasattr(math, '__file__')) # May be False on some platforms
Check first:
1def get_module_path(module):
2 if hasattr(module, '__file__') and module.__file__:
3 return module.__file__
4 return f"{module.__name__} is a built-in module (no file)"
5
6import os, sys
7print(get_module_path(os)) # /usr/lib/python3.11/os.py
8print(get_module_path(sys)) # sys is a built-in module (no file)
Method 2: inspect.getfile()
The inspect module provides a more robust way:
1import inspect
2import os
3import json
4import collections
5
6print(inspect.getfile(os)) # /usr/lib/python3.11/os.py
7print(inspect.getfile(json)) # /usr/lib/python3.11/json/__init__.py
8print(inspect.getfile(collections)) # /usr/lib/python3.11/collections/__init__.py
9
10# Works on classes and functions too
11from pathlib import Path
12print(inspect.getfile(Path)) # /usr/lib/python3.11/pathlib.py
inspect.getfile() raises TypeError for built-in modules:
1import sys
2try:
3 print(inspect.getfile(sys))
4except TypeError as e:
5 print(e) # <module 'sys' (built-in)> is a built-in module
Method 3: From the Command Line
pip show
1# Shows package location and metadata
2pip show requests
3
4# Output:
5# Name: requests
6# Version: 2.31.0
7# Location: /home/user/.local/lib/python3.11/site-packages
python -c
1# One-liner to print module path
2python -c "import requests; print(requests.__file__)"
3# /home/user/.local/lib/python3.11/site-packages/requests/__init__.py
4
5python -c "import numpy; print(numpy.__file__)"
6# /home/user/.local/lib/python3.11/site-packages/numpy/__init__.py
python -m site
1# Show all module search paths
2python -m site
3
4# Output includes:
5# sys.path = [
6# '/home/user/project',
7# '/usr/lib/python3.11',
8# '/usr/lib/python3.11/lib-dynload',
9# '/home/user/.local/lib/python3.11/site-packages',
10# '/usr/lib/python3.11/site-packages',
11# ]
Method 4: sys.path and Import Search Order
Python searches for modules in directories listed in sys.path:
1import sys
2
3for path in sys.path:
4 print(path)
5
6# Output (typical):
7# /home/user/project (current directory)
8# /usr/lib/python3.11 (standard library)
9# /usr/lib/python3.11/lib-dynload
10# /home/user/.local/lib/python3.11/site-packages (user packages)
11# /usr/lib/python3.11/site-packages (system packages)
Python checks these paths in order. The first match wins:
# Which 'json' module is actually imported?
import json
print(json.__file__) # Verifies it's the standard library, not a local file named json.py
Method 5: importlib
1import importlib.util
2
3spec = importlib.util.find_spec('requests')
4if spec:
5 print(spec.origin) # /.../.../requests/__init__.py
6 print(spec.submodule_search_locations) # Package directories
7
8# Check if a module exists without importing it
9spec = importlib.util.find_spec('nonexistent')
10print(spec) # None
Finding the Source Directory
1import os
2import requests
3
4# Get the directory containing the module
5module_dir = os.path.dirname(requests.__file__)
6print(module_dir)
7# /home/user/.local/lib/python3.11/site-packages/requests
8
9# List files in the module directory
10for f in sorted(os.listdir(module_dir)):
11 print(f)
12# __init__.py
13# adapters.py
14# api.py
15# auth.py
16# ...
Virtual Environment Module Locations
1# Activate a virtual environment
2source venv/bin/activate
3
4# Modules install into the venv's site-packages
5python -c "import requests; print(requests.__file__)"
6# /home/user/project/venv/lib/python3.11/site-packages/requests/__init__.py
7
8# Show all paths
9python -c "import site; print(site.getsitepackages())"
10# ['/home/user/project/venv/lib/python3.11/site-packages']
Common Pitfalls
Shadowing standard library modules with local files: A file named json.py or random.py in your project directory shadows the standard library module. Python searches the current directory first, so your local file gets imported instead.
Built-in modules have no __file__: Modules like sys, builtins, and sometimes math are implemented in C and compiled into the interpreter. They have no file path. Check hasattr(module, '__file__') before accessing it.
Confusing .pyc with .py paths: __file__ may point to a .pyc (compiled bytecode) file instead of the .py source. Use importlib.util.find_spec() which reports the source file, or check if a .py file exists at the same path.
Different module paths in virtual environments: A module may be installed globally and in a virtual environment. The path depends on which Python interpreter is active. Always verify with python -c "import x; print(x.__file__)" from the intended environment.
Editable installs (pip install -e) point to the source directory: Editable installs symlink to the development directory. The module path reflects the source location, not site-packages, which can be confusing when debugging import issues.
Summary
Use module.__file__ for the quickest way to find a module's source file
Use inspect.getfile(module) for a more robust alternative that also works on classes and functions
Use pip show package_name from the command line to find package location and metadata
Use importlib.util.find_spec() to find a module path without importing it
Check sys.path to understand the import search order and debug shadowing issues