A practical troubleshooting companion to the Python Wheel Distributions Guide. This guide focuses on solving real problems you’ll encounter during Python package development.
- Quick Diagnostic Reference
- Development Environment Problems
- Package Installation Issues
- Package Structure and Configuration
- Building and Distribution
- CLI Application Specific Issues
- Testing Problems
- Debugging Techniques and Tools
- Advanced Troubleshooting
- Emergency Quick Fixes
- Useful Resources
| Symptom | Most Common Cause | Quick Fix |
|---|---|---|
ModuleNotFoundError after install | Not in virtual environment | source .venv/bin/activate |
| Changes not taking effect | Not in editable mode | pip install -e . |
__version__ shows “unknown” | Package not installed | pip install -e . |
| Command not found | Missing [project.scripts] | Check pyproject.toml |
| Tests can’t import package | Package not installed | pip install -e . |
| Import works in dev, fails in wheel | Missing __init__.py | Add __init__.py files |
| Old code still running | Stale .pyc cache | find . -name "*.pyc" -delete |
| Build includes test files | Wrong package discovery | Check [tool.setuptools.packages.find] |
Symptoms:
- Package installs to system Python
ModuleNotFoundErroreven after install- Wrong Python version running
Diagnosis:
# Check which Python you're using
which python
python --version
# Check if in virtual environment
echo $VIRTUAL_ENV # Should show path to your .venvSolutions:
# Activate virtual environment
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows cmd
.venv\Scripts\Activate.ps1 # Windows PowerShell
# Verify activation
which python # Should point to .venv/bin/pythonProblem: Using Python 2.x or incompatible Python 3.x version
Diagnosis:
python --version
python3 --version
# Check what your project requires
grep "requires-python" pyproject.tomlSolutions:
# Create venv with specific Python version
python3.9 -m venv .venv
python3.10 -m venv .venv
# On systems with multiple Python versions
/usr/bin/python3.11 -m venv .venvSymptoms:
- Install seems to work but package not importable
- Package installed but in different Python
Diagnosis:
# Check where pip installs
pip show pip | grep Location
# Check all Python paths
python -c "import sys; print('\n'.join(sys.path))"
# Find where your package is (if anywhere)
python -c "import your_package; print(your_package.__file__)"Solutions:
# Always use python -m pip instead of bare pip
python -m pip install -e .
# Or explicitly use the venv pip
.venv/bin/pip install -e .Problem: Installed CLI commands not found
Diagnosis:
echo $PATH
# Find where command was installed
find ~/.local -name "your-command"
find .venv -name "your-command"
# For pipx
pipx listSolutions:
# Add Python user scripts to PATH (in ~/.bashrc or ~/.zshrc)
export PATH="$HOME/.local/bin:$PATH"
# For pipx
pipx ensurepath
# Then reload shell or source config
source ~/.bashrcProblem: ModuleNotFoundError: No module named 'your_package'
Root Causes & Solutions:
- Not in virtual environment (MOST COMMON)
# Verify you're in venv echo $VIRTUAL_ENV # Activate if not source .venv/bin/activate
- Wrong directory during install
# Must be in directory containing pyproject.toml ls pyproject.toml # Should exist # Install from correct location pip install -e .
- Incorrect package configuration
# In pyproject.toml - must have both sections [tool.setuptools] package-dir = {"" = "src"} [tool.setuptools.packages.find] where = ["src"]
- Missing __init__.py files
# Every package directory needs __init__.py src/ └── your_package/ ├── __init__.py # REQUIRED └── subpackage/ ├── __init__.py # REQUIRED └── module.py
Verification:
# Check if package is installed
pip list | grep your-package
# Check installation details
pip show your-package
# Verify Python can find it
python -c "import your_package; print(your_package.__file__)"
# Test import manually
python -c "import your_package; print(dir(your_package))"Symptoms:
pip install -e .succeeds but package not importable- Changes not taking effect
Diagnosis:
# Check if actually installed in editable mode
pip show your-package | grep Location
# Should point to your source directory, not site-packages
# Check for .egg-link file
ls .venv/lib/python*/site-packages/*.egg-linkSolutions:
# Clean and reinstall
pip uninstall your-package
rm -rf *.egg-info
pip install -e .
# Force reinstall
pip install -e . --force-reinstall --no-deps
# Verify editable mode
pip list -v | grep your-package # Should show "Editable project location"Problem: Code changes don’t appear when running/testing
Diagnosis:
# Are you in editable mode?
pip show your-package | grep Location
# Check for cached bytecode
find . -name "*.pyc" | head
find . -type d -name "__pycache__" | headSolutions:
- Ensure editable mode is active:
pip install -e . - Clear Python cache:
# Remove all .pyc files and __pycache__ directories find . -type f -name "*.pyc" -delete find . -type d -name "__pycache__" -exec rm -rf {} + # Or use pyclean (if available) pyclean .
- Restart Python interpreter:
- If using interactive Python, quit and restart
- If using Jupyter, restart kernel
- Modules are cached after first import
- Check you’re editing the right file:
# Find which file Python is actually importing python -c "import your_package.module; print(your_package.module.__file__)"
Problem: Package installs but required dependencies missing
Diagnosis:
# Check what dependencies are declared
grep -A 10 "dependencies" pyproject.toml
# Check what's actually installed
pip listSolutions:
- Ensure dependencies listed in pyproject.toml:
[project] dependencies = [ "requests>=2.28.0", "click>=8.0.0", ]
- For development dependencies:
[project.optional-dependencies] dev = [ "pytest>=7.0", "pytest-cov>=3.0", ]
# Install with dev dependencies pip install -e ".[dev]"
- Reinstall to pick up new dependencies:
pip install -e . --force-reinstall
Problem: Package structure seems correct but imports fail
Diagnosis:
# Find all Python packages without __init__.py
find src -type d -name "*" | while read dir; do
if [ -n "$(find "$dir" -maxdepth 1 -name "*.py" -not -name "__init__.py")" ]; then
if [ ! -f "$dir/__init__.py" ]; then
echo "Missing __init__.py: $dir"
fi
fi
doneSolution:
Every directory that should be a Python package MUST have __init__.py:
src/
└── your_package/
├── __init__.py # REQUIRED - can be empty
├── module.py
└── subpackage/
├── __init__.py # REQUIRED - can be empty
└── submodule.py
# Create missing __init__.py files
touch src/your_package/__init__.py
touch src/your_package/subpackage/__init__.pyNote: While Python 3.3+ supports namespace packages without __init__.py, it’s best practice to include them for clarity and compatibility.
Problem: Package not discovered during build
Common Configuration Errors:
- Incorrect package-dir:
# WRONG - missing package-dir [tool.setuptools.packages.find] where = ["src"] # CORRECT - both sections needed [tool.setuptools] package-dir = {"" = "src"} [tool.setuptools.packages.find] where = ["src"]
- Wrong package name in version():
# In __init__.py - name must match [project] name exactly from importlib.metadata import version # WRONG - name mismatch __version__ = version("my_package") # When project name is "my-package" # CORRECT - exact match with pyproject.toml [project] name __version__ = version("my-package")
- Invalid TOML syntax:
# Validate your pyproject.toml pip install validate-pyproject validate-pyproject pyproject.toml
Verification:
# Check what packages setuptools will find
python -c "import setuptools; print(setuptools.find_packages('src'))"
# Should output: ['your_package', 'your_package.subpackage', ...]Problem: Some modules missing from built wheel
Diagnosis:
# Check what's in your wheel
unzip -l dist/*.whl
# Compare with source
find src -name "*.py" | sortCommon Issues:
- Packages not auto-discovered:
# Explicit include if auto-discovery fails [tool.setuptools.packages.find] where = ["src"] include = ["your_package*"] # Explicitly include exclude = ["tests*"] # Explicitly exclude
- Missing data files:
# Include non-Python files [tool.setuptools.package-data] your_package = [ "*.txt", "*.json", "data/**/*", "templates/**/*.html", ]
Problem: __version__ returns “unknown” or raises PackageNotFoundError
Root Causes & Solutions:
- Package not installed (MOST COMMON)
pip install -e . - Package name mismatch:
# In __init__.py from importlib.metadata import version, PackageNotFoundError try: # This name MUST match [project] name in pyproject.toml EXACTLY __version__ = version("exact-package-name-here") except PackageNotFoundError: __version__ = "unknown"
# In pyproject.toml [project] name = "exact-package-name-here" # Must match version() call
- Wrong Python environment:
# Verify environment which python echo $VIRTUAL_ENV # Reinstall in correct environment pip install -e .
- Using wrong metadata API:
# WRONG - old API (deprecated) import pkg_resources __version__ = pkg_resources.get_distribution("package").version # CORRECT - modern API (Python 3.8+) from importlib.metadata import version __version__ = version("package")
Problem: Old code persists in built wheels despite source changes
Symptoms:
- Changes not appearing in wheel
- Old files still in distribution
- Build seems to use cached data
Solution:
# Complete cleanup of all build artifacts
rm -rf build/
rm -rf dist/
rm -rf *.egg-info
rm -rf src/*.egg-info
rm -rf .eggs/
# Clean Python cache
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -exec rm -rf {} +
find . -type d -name ".pytest_cache" -exec rm -rf {} +
rm -rf .coverage htmlcov/
# Now build fresh
python -m buildCreate a cleanup script:
# Save as clean.sh
#!/bin/bash
echo "Cleaning build artifacts..."
rm -rf build/ dist/ *.egg-info src/*.egg-info .eggs/
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -exec rm -rf {} +
echo "Clean complete."Problem: ModuleNotFoundError when using installed wheel, but editable install works fine
This is usually a packaging configuration issue, not a code issue.
Diagnosis:
# Compare what's in the wheel vs. source
echo "=== Source structure ==="
find src -name "*.py" | sort
echo "=== Wheel contents ==="
unzip -l dist/*.whl | grep "\.py$"Common Causes & Solutions:
- Missing __init__.py files:
# Add __init__.py to all package directories find src -type d -name "*" -exec sh -c ' if [ -n "$(find "$1" -maxdepth 1 -name "*.py")" ]; then touch "$1/__init__.py" fi ' sh {} \;
- Package not discovered by setuptools:
# Verify package discovery python -c "import setuptools; print(setuptools.find_packages('src'))" # If your package is missing, check pyproject.toml
- Relative imports that work in editable but fail in wheel:
# WRONG - implicit relative import from module import function # CORRECT - explicit absolute import from your_package.module import function
- Files outside package directory:
# WRONG - code.py won't be included src/ ├── your_package/ │ └── __init__.py └── code.py # Outside package! # CORRECT src/ └── your_package/ ├── __init__.py └── code.py # Inside package
# Update build tools
python -m pip install --upgrade pip setuptools wheel build
# Try build again
python -m build# Install wheel package
pip install wheel
# Use modern build tool instead
pip install build
python -m build# Upgrade setuptools
pip install "setuptools>=42"
# Or update build-system requirements in pyproject.toml
[build-system]
requires = ["setuptools>=42", "wheel"]Problem: Test files in wheel, or data files missing
Diagnosis:
# List wheel contents
unzip -l dist/*.whl
# Extract and inspect
unzip dist/*.whl -d /tmp/inspect
tree /tmp/inspect # or ls -la /tmp/inspectSolutions:
- Tests included in wheel (using src layout prevents this):
# With src layout, tests are automatically excluded project/ ├── src/your_package/ # Only this goes in wheel └── tests/ # Automatically excluded - Missing data files:
[tool.setuptools.package-data] your_package = [ "*.txt", "*.json", "data/**/*", "templates/**/*.html", ]
- Including unwanted files:
[tool.setuptools] exclude-package-data = { "*" = ["*.c", "*.h", "*.so", "*.dylib"] }
Problem: bash: command not found: mycommand after successful install
Root Causes & Solutions:
- Missing [project.scripts] section:
# In pyproject.toml [project.scripts] mycommand = "my_package.cli:main" # └─ package.module:function format
- Entry point function not importable:
# Test if entry point works python -c "from my_package.cli import main; print(main)" # If this fails, the entry point won't work either
- Command installed but not in PATH:
# Find where command was installed find .venv -name "mycommand" find ~/.local -name "mycommand" # Check PATH echo $PATH | tr ':' '\n' # Add to PATH if needed (in ~/.bashrc or ~/.zshrc) export PATH="$HOME/.local/bin:$PATH"
- Reinstall needed after adding entry point:
pip uninstall my-package pip install -e . # Verify command exists which mycommand
Problem: Entry point syntax errors or incorrect references
Correct format:
[project.scripts]
command-name = "package.module:function"
# └─ no .py extension, colon before functionCommon mistakes:
# WRONG - .py extension
mycommand = "my_package.cli.py:main"
# WRONG - dot instead of colon
mycommand = "my_package.cli.main"
# WRONG - incorrect path
mycommand = "src.my_package.cli:main"
# CORRECT
mycommand = "my_package.cli:main"Entry point function requirements:
# In my_package/cli.py
# Option 1: Function that returns exit code
def main():
"""CLI entry point."""
try:
# Your code here
return 0 # Success
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
return 1 # Error
# Option 2: Function that calls sys.exit
def main():
"""CLI entry point."""
try:
# Your code here
sys.exit(0)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
# Don't forget this for direct execution
if __name__ == "__main__":
sys.exit(main())Problem: Command works with pip but not with pipx
Diagnosis:
# Check pipx installation
pipx list
# Check pipx path
pipx environmentSolutions:
- PATH not configured:
# Ensure PATH is set pipx ensurepath # Restart shell or source config source ~/.bashrc
- Conflicting installation:
# Remove pip installation first pip uninstall my-package # Install with pipx pipx install my-package
- Installing from local wheel:
# Build first python -m build # Install with pipx pipx install ./dist/my_package-*.whl # Or install in editable mode pipx install -e .
Problem: ModuleNotFoundError when running pytest
Root Causes & Solutions:
- Package not installed (MOST COMMON):
pip install -e . pytest - Wrong import in tests:
# WRONG - importing from src from src.my_package.module import function # CORRECT - import from package name from my_package.module import function
- Missing pytest configuration:
# In pyproject.toml [tool.pytest.ini_options] pythonpath = ["src"] # Add src to Python path testpaths = ["tests"]
- Running pytest from wrong directory:
# Must run from project root cd /path/to/project # Directory containing pyproject.toml pytest
Problem: All tests pass in editable mode, but wheel doesn’t work
This indicates a packaging issue, not a code issue.
Diagnosis:
# Test the actual wheel in isolation
python -m venv test_env
source test_env/bin/activate
pip install dist/*.whl
# Try importing
python -c "import my_package"
# Clean up
deactivate
rm -rf test_envCommon causes:
- Missing
__init__.pyfiles → See “Code Works in Development but Not in Built Wheel” - Files outside package directory → See “Package Structure and Configuration”
- Incorrect package discovery → Check
pyproject.tomlconfiguration
Problem: Pytest not finding tests or running incorrectly
Diagnosis:
# See what pytest is doing
pytest --collect-only
# Verbose output
pytest -v
# Show which tests pytest finds
pytest --co -vCommon configuration:
[tool.pytest.ini_options]
# Where to find tests
testpaths = ["tests"]
# Test file patterns
python_files = ["test_*.py", "*_test.py"]
# Test class patterns
python_classes = ["Test*"]
# Test function patterns
python_functions = ["test_*"]
# Add src to path (if package not installed)
pythonpath = ["src"]
# Default options
addopts = "-v --strict-markers"# Show package information
pip show package-name
# List all files in package
pip show -f package-name
# Check if package is editable
pip list -v | grep package-name
# Find package location
python -c "import package; print(package.__file__)"
# Show package contents
python -c "import package; print(dir(package))"
# Check version
python -c "import package; print(package.__version__)"# Show sys.path (where Python looks for imports)
python -c "import sys; print('\n'.join(sys.path))"
# Show sys.path with line numbers
python -c "import sys; import pprint; pprint.pprint(list(enumerate(sys.path)))"
# Check if module is findable
python -c "import importlib.util; print(importlib.util.find_spec('your_package'))"
# Show all installed packages locations
pip list -v# Verbose pip install
pip install -v .
pip install -vv . # Even more verbose
pip install -vvv . # Maximum verbosity
# Verbose build
python -m build -v
# See what setuptools is doing
python -m setuptools.build_meta -v# Validate pyproject.toml
pip install validate-pyproject
validate-pyproject pyproject.toml
# Check Python files for syntax errors
python -m py_compile src/your_package/*.py
python -m compileall src/
# Lint pyproject.toml
pip install toml-sort
toml-sort --check pyproject.toml
# Check package metadata
pip install twine
twine check dist/*# Debug import issues interactively
python -v -c "import your_package" # Shows all imports
# Or in Python:
import sys
sys.path.insert(0, '/path/to/debug') # Temporarily add path
# See what gets imported
import importlib
import your_package
print(importlib.util.find_spec('your_package'))
print(your_package.__file__)
print(your_package.__path__) # For packages# List wheel contents
unzip -l dist/*.whl
# Extract wheel
unzip dist/*.whl -d /tmp/wheel_inspect
# View wheel metadata
unzip -p dist/*.whl "*/METADATA" | less
# Check wheel tags
pip install wheel
wheel unpack dist/*.whlProblem: ImportError: cannot import name 'X' from partially initialized module
Solutions:
- Move import inside function:
# Instead of module-level import def function(): from .other_module import something # Import here # Use something
- Restructure code to break cycle:
# Move shared code to separate module module_a.py → module_a.py module_b.py → module_b.py → shared.py # New module
Problem: ImportError: attempted relative import with no known parent package
Solutions:
# WRONG - relative imports in scripts
if __name__ == "__main__":
from .module import function # Fails!
# CORRECT - use absolute imports
if __name__ == "__main__":
from package.module import function
# Or run as module
python -m package.script # Instead of python package/script.pyProblem: Package requires conflicting versions of dependencies
Diagnosis:
# Check dependency tree
pip install pipdeptree
pipdeptree
# Show conflicts
pipdeptree --warn conflict
# Check what would be installed
pip install --dry-run your-packageSolutions:
- Use more flexible version constraints:
# Too strict dependencies = ["requests==2.28.0"] # Better - allows range dependencies = ["requests>=2.28.0,<3.0.0"]
- Create isolated environment:
# Use pipx for CLI tools (isolation guaranteed) pipx install your-package # Or use dedicated venv python -m venv isolated_env source isolated_env/bin/activate pip install your-package
# Windows uses backslashes
.venv\Scripts\activate
# In code, use pathlib for cross-platform paths
from pathlib import Path
config_file = Path(__file__).parent / "config.json"# Don't use sudo with pip
# WRONG: sudo pip install package
# CORRECT: Use virtual environment
python3 -m venv .venv
source .venv/bin/activate
pip install package# Some distros separate python3-venv
# Debian/Ubuntu
sudo apt install python3-venv
# Fedora
sudo dnf install python3-venv
# Check if venv is available
python3 -m venv --helpProblem: Build fails with cryptic setuptools errors
Diagnosis:
# Check build backend version
pip show setuptools build wheel
# Try different build backend
pip install hatchling # Alternative to setuptoolsSolutions:
- Update build tools:
pip install --upgrade setuptools build wheel pip - Pin build requirements:
[build-system] requires = [ "setuptools>=65.0", "wheel>=0.38.0", ]
- Switch build backend (if needed):
# Use hatchling instead of setuptools [build-system] requires = ["hatchling"] build-backend = "hatchling.build"
When everything fails, try these in order:
# 1. Nuclear option - start completely fresh
deactivate # If in venv
rm -rf .venv
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip setuptools wheel build
# 2. Clean everything
rm -rf build/ dist/ *.egg-info src/*.egg-info .eggs/
find . -type f -name "*.pyc" -delete
find . -type d -name "__pycache__" -exec rm -rf {} +
# 3. Reinstall package
pip install -e .
# 4. Verify
python -c "import your_package; print(your_package.__version__)"
pytest- Python Packaging User Guide: https://packaging.python.org
- Setuptools Documentation: https://setuptools.pypa.io
- Build Tool: https://pypa-build.readthedocs.io
- pip Documentation: https://pip.pypa.io
- Python Import System: https://docs.python.org/3/reference/import.html
- Troubleshooting on Stack Overflow: https://stackoverflow.com/questions/tagged/python-packaging