Project Structure¶
โ Voltar para Python | ๐ Home
src/
layout,.gitignore
, andREADME
patterns that scale from scripts to packages.
A well-organized project structure makes code easier to find, test, and maintain. This guide covers modern Python project organization that works for everything from simple scripts to complex packages.
๐ฏ Goals¶
- Consistent structure across all Python projects
- Scalable organization from scripts to packages
- Clean separation of source code, tests, and documentation
- Modern conventions that work with current tooling
๐ Quick Verification¶
A well-structured Python project should have:
# Check project structure
ls -la
# Should see: src/, tests/, README.md, .gitignore, requirements.txt
# Verify package installation works
pip install -e .
# Should install without errors
# Test import works
python -c "import your_package_name"
# Should import without errors
๐ Recommended Structure¶
Modern Python Project Layout¶
my-python-project/
โโโ .gitignore # Git ignore patterns
โโโ .python-version # Python version for pyenv
โโโ README.md # Project documentation
โโโ pyproject.toml # Modern Python configuration
โโโ requirements.txt # Dependencies (or use pyproject.toml)
โโโ src/ # Source code directory
โ โโโ my_package/ # Main package
โ โโโ __init__.py
โ โโโ main.py # Entry point
โ โโโ core/ # Core functionality
โ โ โโโ __init__.py
โ โ โโโ utils.py
โ โโโ data/ # Data processing
โ โโโ __init__.py
โ โโโ loader.py
โโโ tests/ # Test files
โ โโโ __init__.py
โ โโโ test_main.py
โ โโโ test_utils.py
โโโ docs/ # Documentation (optional)
โ โโโ index.md
โโโ scripts/ # Utility scripts (optional)
โโโ setup.py
Why src/
Layout?¶
The src/
layout is preferred because: - Prevents accidental imports during development - Forces proper installation testing - Clear separation between source and other files - Industry standard for Python packages
๐ Setting Up a New Project¶
1. Create Directory Structure¶
# Create project directory
mkdir my-python-project && cd my-python-project
# Create main directories
mkdir -p src/my_package/core src/my_package/data
mkdir tests docs scripts
# Create Python files
touch src/my_package/__init__.py
touch src/my_package/main.py
touch src/my_package/core/__init__.py
touch src/my_package/core/utils.py
touch tests/__init__.py
touch tests/test_main.py
2. Essential Configuration Files¶
.python-version
(pyenv)¶
.gitignore
(Python-specific)¶
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# Virtual environments
.venv/
venv/
env/
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Testing
.pytest_cache/
.coverage
htmlcov/
# Distribution / packaging
build/
dist/
*.egg-info/
# Environment variables
.env
.env.local
pyproject.toml
(Modern configuration)¶
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-package"
version = "0.1.0"
description = "A sample Python package"
authors = [
{name = "Your Name", email = "your.email@example.com"},
]
dependencies = [
"pandas>=2.0.0",
"numpy>=1.24.0",
]
requires-python = ">=3.11"
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=23.0",
"ruff>=0.1.0",
]
[project.scripts]
my-package = "my_package.main:main"
[tool.setuptools.packages.find]
where = ["src"]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
[tool.black]
line-length = 88
target-version = ['py311']
[tool.ruff]
line-length = 88
target-version = "py311"
README.md
Template¶
# My Python Project
Brief description of what your project does.
## Installation
```bash
# Clone repository
git clone https://github.com/username/my-python-project.git
cd my-python-project
# Create virtual environment
uv venv .venv
source .venv/bin/activate
# Install package in development mode
pip install -e .
Usage¶
Development¶
# Install development dependencies
pip install -e .[dev]
# Run tests
pytest
# Format code
black src/ tests/
# Lint code
ruff check src/ tests/
License¶
MIT License - see LICENSE file for details.
## ๐ Code Organization Patterns
### Package `__init__.py`
```python
"""My Package - A sample Python package."""
from .main import main_function
from .core.utils import utility_function
__version__ = "0.1.0"
__all__ = ["main_function", "utility_function"]
Main Module Pattern¶
# src/my_package/main.py
"""Main module with CLI entry point."""
import argparse
from .core.utils import process_data
def main():
"""Main function - entry point for CLI."""
parser = argparse.ArgumentParser(description="My Python Package")
parser.add_argument("--input", "-i", help="Input file")
parser.add_argument("--output", "-o", help="Output file")
args = parser.parse_args()
if args.input:
result = process_data(args.input)
print(f"Processed: {result}")
if __name__ == "__main__":
main()
Utility Module Pattern¶
# src/my_package/core/utils.py
"""Utility functions for data processing."""
from pathlib import Path
from typing import Union
def process_data(input_path: Union[str, Path]) -> dict:
"""Process input data and return results.
Args:
input_path: Path to input file
Returns:
Dictionary with processed results
"""
input_path = Path(input_path)
if not input_path.exists():
raise FileNotFoundError(f"Input file not found: {input_path}")
# Processing logic here
return {"status": "processed", "file": str(input_path)}
๐งช Testing Structure¶
Test Organization¶
# tests/test_main.py
"""Tests for main module."""
import pytest
from my_package.main import main_function
def test_main_function():
"""Test main function works correctly."""
result = main_function("test_input")
assert result is not None
assert isinstance(result, dict)
def test_main_function_with_invalid_input():
"""Test main function handles invalid input."""
with pytest.raises(ValueError):
main_function(None)
Running Tests¶
# Install test dependencies
pip install -e .[dev]
# Run all tests
pytest
# Run with coverage
pytest --cov=src/my_package --cov-report=html
# Run specific test file
pytest tests/test_main.py
# Run with verbose output
pytest -v
๐ง Development Workflow¶
1. Initial Setup¶
mkdir new-project && cd new-project
echo "3.12" > .python-version
uv venv .venv
source .venv/bin/activate
2. Create Structure¶
mkdir -p src/my_package tests
touch src/my_package/__init__.py
touch src/my_package/main.py
# ... create other files
3. Install in Development Mode¶
4. Development Cycle¶
# Make changes to code
# Run tests
pytest
# Format code
black src/ tests/
# Check linting
ruff check src/ tests/
# Install new dependencies
uv pip install new-package
๐ Related Sections¶
- Install & Setup - Setting up pyenv and uv
- Environments & Dependencies - Managing virtual environments
- Testing - pytest fundamentals and best practices
- CLI Basics - Building command-line interfaces
โก Quick Reference¶
New Project Checklist: - [ ] Create src/package_name/
directory - [ ] Add __init__.py
files - [ ] Create tests/
directory
- [ ] Set up .gitignore
- [ ] Write README.md
- [ ] Configure pyproject.toml
- [ ] Install with pip install -e .
Daily Commands:
pip install -e . # Install in development mode
pytest # Run tests
black src/ tests/ # Format code
ruff check src/ tests/ # Lint code
Next: Learn about Environments & Dependencies for advanced environment management, or jump to Testing to set up proper testing workflows.