A modern Python monorepo template using uv for workspace management and dependency resolution. This template supports libraries, applications, and CLI tools with shared tooling and configuration.
Using this as a template? See TEMPLATE.md for setup instructions and the automated setup script.
- Unified Workspace Management: Single
uvworkspace for all packages - Shared Tooling: Centralized linting (ruff), testing (pytest), and CI/CD configuration
- Independent Publishing: Each package can be published separately to PyPI
- Cross-Package Dependencies: Packages can depend on each other using workspace sources
- Efficient CI/CD: GitHub Actions workflows for linting, testing, and publishing
- Modern Python Packaging: PEP 621 (pyproject.toml) and PEP 735 (dependency-groups)
python-monorepo/
├── .github/workflows/ # GitHub Actions workflows
│ ├── lint.yml # Linting workflow
│ ├── test.yml # Testing workflow
│ └── publish.yml # Publishing workflow
├── packages/ # Workspace members
│ ├── my-library/ # Reusable library
│ │ ├── src/my_library/ # Package source code
│ │ ├── tests/ # Package tests
│ │ └── pyproject.toml # Package configuration
│ ├── my-app/ # Application using the library
│ │ ├── src/my_app/
│ │ ├── tests/
│ │ └── pyproject.toml
│ └── my-cli/ # CLI tool using the library
│ ├── src/my_cli/
│ ├── tests/
│ └── pyproject.toml
├── pyproject.toml # Root workspace configuration
├── ruff.toml # Shared linting configuration
├── .gitignore # Git ignore rules
└── README.md # This file
- Python 3.11 or later
- uv (install with
curl -LsSf https://astral.sh/uv/install.sh | sh)
-
Clone the repository:
git clone <repository-url> cd python-monorepo
-
Sync dependencies:
uv sync
This installs all workspace members and development dependencies in an isolated environment.
All commands should be run from the workspace root using uv run:
# Run linting
uv run ruff check .
uv run ruff format .
# Run tests
uv run pytest
# Run tests with coverage
uv run pytest --cov=packagesTo work with a specific package:
# Install only a specific package
uv sync --package my-library
# Run tests for a specific package
uv run pytest packages/my-library/testsTo add a dependency to a package:
# Add a regular dependency
uv add -p packages/my-library requests
# Add a development dependency
uv add -p packages/my-library --group dev pytest-mockTo add a workspace package as a dependency:
Edit the dependencies field in the package's pyproject.toml:
[project]
dependencies = [
"my-library>=0.1.0",
]The dependency will be resolved from the workspace source automatically.
-
Create the package directory structure:
mkdir -p packages/my-new-package/{src/my_new_package,tests} -
Create
packages/my-new-package/pyproject.toml:[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "my-new-package" version = "0.1.0" description = "Description here" requires-python = ">=3.11" # ... other metadata
-
Add the package to the root
pyproject.toml:[tool.uv] workspace = [ { path = "packages/my-library" }, { path = "packages/my-app" }, { path = "packages/my-cli" }, { path = "packages/my-new-package" }, # New package ]
-
Run
uv syncto update the workspace
Configuration is centralized in ruff.toml. All packages use the same linting rules.
# Check code style
uv run ruff check .
# Format code
uv run ruff format .
# Check formatting without changes
uv run ruff format --check .All tests use pytest. Configuration is in the root pyproject.toml.
# Run all tests
uv run pytest
# Run tests for a specific package
uv run pytest packages/my-library
# Run tests with coverage
uv run pytest --cov=packages
# Run a specific test file
uv run pytest packages/my-library/tests/test_example.pyThe monorepo includes GitHub Actions workflows:
- lint.yml: Runs
ruff checkandruff format --checkon every push and PR - test.yml: Runs pytest on Python 3.11, 3.12, and 3.13
- publish.yml: Publishes packages to PyPI on version tags
Tag a release with one of these formats:
# Publish all packages
git tag v0.1.0
git push origin v0.1.0
# Publish a specific package
git tag my-library-v0.1.0
git push origin my-library-v0.1.0The publishing workflow will:
- Build the package
- Publish to PyPI using trusted publishing (no secrets required)
A reusable library with basic math functions:
add(a, b): Add two numbersmultiply(a, b): Multiply two numbers
An application that uses my-library to process lists of numbers:
process_numbers(numbers): Calculate sum and product of a list
A CLI tool for mathematical operations:
my-cli add-numbers A B: Add two numbersmy-cli multiply-numbers A B: Multiply two numbersmy-cli sum-all NUMS...: Sum multiple numbersmy-cli multiply-all NUMS...: Multiply multiple numbers
- Use workspace dependencies: When possible, depend on other workspace packages rather than external packages
- Consistent versioning: Keep related packages at similar versions
- Shared configuration: Use the root configuration for common settings
- Test coverage: Write tests for all packages, aim for >80% coverage
- Type hints: Use Python type hints for better IDE support and documentation
- Meaningful commits: Write descriptive commit messages that explain the "why"
Make sure you've run uv sync to install the workspace:
uv syncCheck the uv.lock file is up to date:
uv lock --upgradeEnsure tests are in the tests/ directory following the naming convention test_*.py.
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes and add tests
- Run linting and tests:
uv run ruff check . && uv run pytest - Commit with descriptive messages
- Push and create a pull request
MIT - See LICENSE file for details