Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Winipedia/pyrig/llms.txt
Use this file to discover all available pages before exploring further.
Overview
pyrig uses the uv_build backend for packaging Python projects. This modern build system integrates seamlessly with uv and provides:
- Fast builds: Rust-based backend for speed
- Console script integration: Automatic CLI entry points
- Standard compliance: PEP 517/518 compatible
- Simple configuration: Minimal
pyproject.toml setup
- uv ecosystem: Works with the entire uv toolchain
This guide covers packaging your pyrig-based projects and extensions.
Understanding uv_build
What is uv_build?
uv_build is a PEP 517-compliant build backend that:
- Builds wheels and source distributions (sdists)
- Integrates with
pyproject.toml
- Handles console script generation automatically
- Works with uv’s dependency management
How pyrig Uses It
Every pyrig project includes this in pyproject.toml:
[build-system]
requires = ["uv_build"]
build-backend = "uv_build"
[tool.uv.build-backend]
module-name = "myproject"
module-root = ""
This configuration:
- Declares
uv_build as the build backend
- Specifies your project’s module name
- Sets the module root (usually empty string for root-level packages)
Packaging a Standard Project
Step 1: Verify pyproject.toml
Ensure your pyproject.toml has the required sections:
[project]
name = "my-project"
version = "1.0.0"
description = "My awesome project"
readme = "README.md"
license = "MIT"
requires-python = ">=3.12"
dependencies = [
"pyrig>=10.0.0",
]
[project.scripts]
my-project = "my_project.rig.cli.cli:main"
[build-system]
requires = ["uv_build"]
build-backend = "uv_build"
[tool.uv.build-backend]
module-name = "my_project"
module-root = ""
Step 2: Build the Package
Use uv build to create distributions:
# Build wheel and sdist
uv build
# Output:
# dist/
# my_project-1.0.0-py3-none-any.whl
# my_project-1.0.0.tar.gz
The build creates:
- Wheel (
.whl): Binary distribution for fast installation
- Source distribution (
.tar.gz): Source code archive
Step 3: Test the Package
Test locally before publishing:
# Install from local wheel
uv pip install dist/my_project-1.0.0-py3-none-any.whl
# Test the CLI
my-project --help
# Test the package
python -c "import my_project; print(my_project.__version__)"
Step 4: Publish to PyPI
Publish using uv publish:
# Publish to PyPI (requires token)
uv publish --token $PYPI_TOKEN
# Publish to Test PyPI
uv publish --token $TEST_PYPI_TOKEN --registry https://test.pypi.org/legacy/
Packaging a pyrig Extension
Extension packages (like personal pyrig packages) have special considerations.
Example: Personal pyrig Package
Create my-pyrig/pyproject.toml:
[project]
name = "my-pyrig"
version = "1.0.0"
description = "My personal pyrig extensions and standards"
readme = "README.md"
license = "MIT"
requires-python = ">=3.12"
# Declare dependency on pyrig
dependencies = [
"pyrig>=10.0.0",
]
# Optional: Provide a CLI for your extensions
[project.scripts]
my-pyrig = "my_pyrig.rig.cli.cli:main"
[build-system]
requires = ["uv_build"]
build-backend = "uv_build"
[tool.uv.build-backend]
module-name = "my_pyrig"
module-root = ""
# Development dependencies
[dependency-groups]
dev = [
"pytest>=9.0.0",
"ruff>=0.15.0",
]
Package Structure
Your extension package should include:
my-pyrig/
├── my_pyrig/
│ ├── __init__.py # Package marker
│ ├── rig/
│ │ ├── __init__.py
│ │ ├── tools/ # Tool overrides
│ │ │ ├── __init__.py
│ │ │ └── linter.py
│ │ ├── configs/ # Config overrides
│ │ │ ├── __init__.py
│ │ │ └── company_config.py
│ │ └── cli/
│ │ ├── __init__.py
│ │ └── subcommands/ # Custom commands
│ │ ├── __init__.py
│ │ └── deploy.py
│ └── src/ # Shared utilities
│ ├── __init__.py
│ └── utils.py
├── tests/
│ └── __init__.py
├── README.md
├── LICENSE
└── pyproject.toml
Important: _init_.py Files
Every directory must have __init__.py for proper package discovery:
# Create all __init__.py files
find my_pyrig -type d -exec touch {}/__init__.py \;
# Or use pyrig
uv run pyrig mkinits
Console Scripts
Console scripts are CLI entry points automatically created during installation.
Defining Scripts
In pyproject.toml:
[project.scripts]
my-tool = "my_project.rig.cli.cli:main"
my-util = "my_project.utils:run"
This creates:
my-tool command → calls my_project.rig.cli.cli.main()
my-util command → calls my_project.utils.run()
Entry Point Function
The entry point must be a callable:
# my_project/rig/cli/cli.py
import typer
app = typer.Typer()
@app.command()
def hello(name: str):
"""Greet someone."""
typer.echo(f"Hello {name}!")
def main():
"""Entry point for console script."""
app()
if __name__ == "__main__":
main()
After installation:
my-tool hello World
# Output: Hello World!
pyrig’s CLI Pattern
pyrig projects use a standard CLI structure:
# my_project/rig/cli/cli.py
import typer
from pyrig.rig.cli.subcommands import discover_subcommands
app = typer.Typer(
name="my-project",
help="My project CLI",
)
# Discover and register all subcommands automatically
discover_subcommands(app, "my_project")
def main():
"""Entry point."""
app()
Subcommands are discovered from my_project/rig/cli/subcommands/:
# my_project/rig/cli/subcommands/deploy.py
import typer
def deploy(env: str) -> None:
"""Deploy to environment."""
typer.echo(f"Deploying to {env}...")
Automatically available:
my-project deploy production
Advanced Configuration
Module Root Configuration
If your package is not at the repository root:
[tool.uv.build-backend]
module-name = "myproject"
module-root = "src" # Package is in src/myproject/
Directory structure:
my-repo/
├── src/
│ └── myproject/
│ ├── __init__.py
│ └── ...
└── pyproject.toml
Include/Exclude Files
Control what’s included in the distribution:
[tool.uv.build-backend]
module-name = "myproject"
module-root = ""
# Exclude patterns
exclude = [
"tests/",
"*.pyc",
"__pycache__/",
".git/",
]
# Include additional files
include = [
"LICENSE",
"README.md",
"my_project/resources/*.json",
]
Version Management
pyrig projects can use dynamic versioning:
[project]
name = "my-project"
version = "1.0.0" # Or use dynamic versioning
To bump the version:
# Bump patch version (1.0.0 → 1.0.1)
uv version --bump patch
# Bump minor version (1.0.0 → 1.1.0)
uv version --bump minor
# Bump major version (1.0.0 → 2.0.0)
uv version --bump major
Dependency Management
Specifying Dependencies
[project]
dependencies = [
"pyrig>=10.0.0",
"requests>=2.28.0",
"pydantic>=2.0.0",
]
[dependency-groups]
dev = [
"pytest>=9.0.0",
"ruff>=0.15.0",
"mypy>=1.0.0",
]
test = [
"pytest-cov>=4.0.0",
"pytest-mock>=3.0.0",
]
Dependency Chain
When packaging extensions, dependencies cascade:
pyrig (base)
↓ requires: typer
my-pyrig (extension)
↓ requires: pyrig, custom-lib
my-project (uses extension)
↓ requires: my-pyrig, fastapi
Installing my-project automatically installs the entire chain!
Optional Dependencies
[project.optional-dependencies]
docs = [
"mkdocs>=1.5.0",
"mkdocs-material>=9.0.0",
]
cloud = [
"boto3>=1.26.0",
"google-cloud-storage>=2.0.0",
]
Install with:
uv pip install my-project[docs]
uv pip install my-project[cloud]
uv pip install my-project[docs,cloud]
Building for Distribution
Local Development Build
For testing during development:
# Install in editable mode
uv pip install -e .
# Make changes to code
vim my_project/rig/tools/custom.py
# Changes are immediately available
my-project --help # Uses latest code
Production Build
For distribution:
# Clean previous builds
rm -rf dist/ build/ *.egg-info
# Build fresh
uv build
# Verify contents
tar -tzf dist/my_project-1.0.0.tar.gz | head -20
unzip -l dist/my_project-1.0.0-py3-none-any.whl | head -20
Automated Builds
In CI/CD (GitHub Actions):
name: Build and Publish
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Build package
run: uv build
- name: Publish to PyPI
env:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
run: uv publish --token $PYPI_TOKEN
Publishing Strategies
Test PyPI First
Always test on Test PyPI before production:
# Build
uv build
# Publish to Test PyPI
uv publish \
--token $TEST_PYPI_TOKEN \
--registry https://test.pypi.org/legacy/
# Test installation
uv pip install \
--index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
my-project
# If everything works, publish to real PyPI
uv publish --token $PYPI_TOKEN
Version Strategy
Use semantic versioning:
- Major (1.0.0 → 2.0.0): Breaking changes
- Minor (1.0.0 → 1.1.0): New features, backward compatible
- Patch (1.0.0 → 1.0.1): Bug fixes
# After bug fix
uv version --bump patch
git add pyproject.toml
git commit -m "Bump version to $(uv version --short)"
git tag "v$(uv version --short)"
git push --tags
Private Package Indexes
For internal packages:
# Publish to private index
uv publish \
--token $PRIVATE_TOKEN \
--registry https://pypi.internal.company.com/
# Install from private index
uv pip install \
--index-url https://pypi.internal.company.com/simple/ \
my-private-package
Configure in pyproject.toml:
[tool.uv]
index-url = "https://pypi.internal.company.com/simple/"
extra-index-url = ["https://pypi.org/simple/"]
Overriding Build Backend
You can override the build backend in your extensions:
Using Poetry
# my_pyrig/rig/tools/package_manager.py
from pyrig.rig.tools.package_manager import PackageManager as PyrigPM
class PackageManager(PyrigPM):
"""Override to use Poetry instead of uv."""
def build_system_requires(self) -> list[str]:
"""Use Poetry for builds."""
return ["poetry-core"]
def build_backend(self) -> str:
"""Poetry build backend."""
return "poetry.core.masonry.api"
Now all projects using your extension will use Poetry!
class PackageManager(PyrigPM):
def build_system_requires(self) -> list[str]:
return ["setuptools>=68.0", "wheel"]
def build_backend(self) -> str:
return "setuptools.build_meta"
Using Hatch
class PackageManager(PyrigPM):
def build_system_requires(self) -> list[str]:
return ["hatchling"]
def build_backend(self) -> str:
return "hatchling.build"
Common Issues and Solutions
Issue: Module Not Found
ModuleNotFoundError: No module named 'my_project'
Solution: Ensure module-name matches your package directory:
[tool.uv.build-backend]
module-name = "my_project" # Must match my_project/ directory
Issue: Console Script Not Working
my-tool: command not found
Solution: Check entry point syntax:
[project.scripts]
my-tool = "my_project.cli:main" # module.submodule:function
Verify the function exists:
# my_project/cli.py
def main():
print("Hello!")
Issue: Missing Files in Distribution
Solution: Include them explicitly:
[tool.uv.build-backend]
include = [
"my_project/data/*.json",
"my_project/templates/*.html",
]
Issue: Import Errors After Install
Solution: Ensure all directories have __init__.py:
Next Steps
Best Practices
- Version carefully: Use semantic versioning
- Test locally: Install from wheel before publishing
- Use Test PyPI: Verify everything works before production
- Include LICENSE: Always include license file
- Write good README: Help users get started
- Document dependencies: Explain what your package needs
- Tag releases: Use git tags for version tracking
- Automate: Use CI/CD for consistent builds
- Keep it simple: Minimal configuration is best
- Follow conventions: Study pyrig’s own
pyproject.toml