Project Structure¶
This guide explains the recommended project structure used in this Python
package template, including the rationale behind the src/ layout and how to
organize your code effectively.
Overview¶
This template follows modern Python packaging best practices with a src/
layout. This structure separates your package code from development files and
tools, providing better isolation and avoiding common packaging issues.
Directory Structure¶
your-project/
├── src/
│ └── your_package/
│ ├── __init__.py
│ ├── __main__.py
│ ├── cli/
│ │ ├── __init__.py
│ │ └── main.py
│ ├── utils/
│ │ ├── __init__.py
│ │ └── log.py
│ └── other_modules.py
├── tests/
│ ├── conftest.py
│ └── test_*.py
├── docs/
│ ├── index.md
│ ├── user_guide/
│ └── dev/
├── .github/
│ └── workflows/
├── pyproject.toml
├── .pre-commit-config.yaml
├── zensical.toml
└── other config files...
Why src/ Layout¶
The src/ layout offers several advantages:
- Clean separation: Package code is isolated from development files
- Avoid import issues: Prevents accidental imports of development dependencies
- Better testing: Easier to test the installed package vs. local development version
- Standard practice: Recommended by Python packaging guides
Package Organization¶
Core Package (src/your_package/)¶
__init__.py: Package initialization, imports, and__version____main__.py: Entry point forpython -m your_package- Modules: Your main code files
- Subpackages: Organized by functionality (e.g.,
cli/,utils/)
Example Structure Breakdown¶
__init__.py¶
"""Top-level package for your_package."""
from __future__ import annotations
from your_package.main_module import main_function
from your_package.version import __version__
__all__ = ["__version__", "main_function"]
- Imports key functions/classes for easy access
- Defines
__all__for controlled exports - Includes version information
__main__.py¶
"""Main entry point for the package."""
from __future__ import annotations
if __name__ == "__main__":
from your_package.cli.main import app
app()
- Allows running with
python -m your_package - Imports and runs the CLI app
CLI Structure (cli/)¶
cli/
├── __init__.py
└── main.py
main.py: Typer/Fire CLI application- Keeps CLI logic separate from core functionality
Utils (utils/)¶
utils/
├── __init__.py
└── helpers.py
- Shared utility functions
- Logging, configuration, etc.
Tests Organization¶
tests/ Directory¶
conftest.py: Shared fixtures and configurationtest_*.py: Test files matching module names- Structure mirrors package:
tests/test_utils.pyforsrc/your_package/utils/
Example Test Structure¶
# tests/conftest.py
import pytest
@pytest.fixture
def sample_data():
return {"key": "value"}
# tests/test_main_module.py
from your_package.main_module import main_function
def test_main_function(sample_data):
result = main_function(sample_data)
assert result is not None
Documentation Structure¶
docs/ Directory¶
index.md: Home pageuser_guide/: User-facing guidesdev/: Developer documentationreference/: Auto-generated API docs
Configuration Files¶
Root Level Files¶
pyproject.toml: Packaging and tool configuration.pre-commit-config.yaml: Pre-commit hookszensical.toml: Documentation configuration.github/workflows/: CI/CD pipelines
Customizing the Structure¶
Renaming the Package¶
- Rename
src/python_package_template/tosrc/your_package_name/ - Update all imports throughout the codebase
- Update
pyproject.tomlname and entry points - Update
zensical.tomland documentation references
Adding New Modules¶
- Create new
.pyfiles insrc/your_package/ - Add imports to
__init__.pyif needed - Create corresponding tests in
tests/ - Update documentation
Adding Subpackages¶
- Create new directories under
src/your_package/ - Add
__init__.pyto each subpackage - Organize related functionality
- Update imports and tests
Best Practices¶
Import Organization¶
- Use absolute imports within the package
- Avoid relative imports where possible
- Keep
__init__.pyfiles minimal
File Naming¶
- Use
snake_casefor modules and functions - Prefix test files with
test_ - Use descriptive names
Code Organization¶
- Group related functionality in modules
- Keep functions/classes focused on single responsibilities
- Use subpackages for complex features
Migration from Flat Layout¶
If migrating from a flat layout (no src/):
- Move package code into
src/your_package/ - Update all import statements
- Adjust tool configurations (e.g., pytest
pythonpath) - Update documentation paths
Common Issues¶
- Import errors: Ensure
src/is in Python path during development - Test discovery: Configure pytest with correct paths
- Documentation: Update API reference generation for new structure
For more information, see the Python Packaging Guide and src layout discussion.