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.
Custom Configuration Files
Learn how to create custom configuration files that integrate seamlessly with pyrig’s configuration system.
Overview
Pyrig’s configuration system automatically discovers and manages any ConfigFile subclass in your project. Create custom configurations for:
- Application-specific settings
- Custom tool configurations
- Project templates
- CI/CD configurations
- Documentation files
Basic Custom Configuration
Step 1: Choose a Base Class
Select the appropriate base class for your file format:
TomlConfigFile - For TOML files (.toml)
YamlConfigFile - For YAML files (.yaml, .yml)
JsonConfigFile - For JSON files (.json)
PythonConfigFile - For Python files (.py)
MarkdownConfigFile - For Markdown files (.md)
StringConfigFile - For plain text files (.txt, .env, etc.)
Step 2: Create Your Config Class
Create a new file in your project’s rig/configs/ directory:
# myapp/rig/configs/myconfig.py
from pathlib import Path
from pyrig.rig.configs.base.toml import TomlConfigFile
from pyrig.rig.configs.base.base import ConfigDict
class MyConfigFile(TomlConfigFile):
"""Manages myconfig.toml for application settings."""
def parent_path(self) -> Path:
"""Place in project root."""
return Path()
def _configs(self) -> ConfigDict:
"""Define expected configuration."""
return {
"app": {
"name": "myapp",
"version": "1.0.0",
"debug": False,
},
"database": {
"host": "localhost",
"port": 5432,
},
}
Step 3: Initialize
Run uv run pyrig mkroot to create the file:
This creates myconfig.toml with your default configuration.
TOML Configuration Example
Database Configuration
# myapp/rig/configs/database.py
from pathlib import Path
from pyrig.rig.configs.base.toml import TomlConfigFile
from pyrig.rig.configs.base.base import ConfigDict
class DatabaseConfigFile(TomlConfigFile):
"""Manages database.toml configuration."""
def parent_path(self) -> Path:
"""Place in config/ directory."""
return Path("config")
def _configs(self) -> ConfigDict:
"""Define database configuration."""
return {
"database": {
"host": "localhost",
"port": 5432,
"name": "myapp",
"user": "postgres",
"pool": {
"min_size": 10,
"max_size": 20,
"timeout": 30,
},
},
"redis": {
"host": "localhost",
"port": 6379,
"db": 0,
},
}
Generates config/database.toml:
[database]
host = "localhost"
port = 5432
name = "myapp"
user = "postgres"
[database.pool]
min_size = 10
max_size = 20
timeout = 30
[redis]
host = "localhost"
port = 6379
db = 0
YAML Configuration Example
Custom GitHub Workflow
# myapp/rig/configs/workflows/custom.py
from pathlib import Path
from pyrig.rig.configs.base.workflow import WorkflowConfigFile
from pyrig.rig.configs.base.base import ConfigDict
class CustomWorkflowConfigFile(WorkflowConfigFile):
"""Custom workflow for deployment."""
def workflow_triggers(self) -> ConfigDict:
"""Trigger on tag push."""
triggers = super().workflow_triggers()
triggers.update({
"push": {
"tags": ["v*"]
}
})
return triggers
def jobs(self) -> ConfigDict:
"""Define deployment job."""
return self.job(
job_func=self.jobs,
steps=[
self.step_checkout_repository(),
self.step_setup_package_manager(python_version="3.13"),
self.step(
step_func=self.step_deploy,
run="echo 'Deploying application'",
),
],
)
def step_deploy(self) -> dict:
"""Deployment step."""
return self.step(
step_func=self.step_deploy,
run="./deploy.sh",
)
JSON Configuration Example
API Configuration
# myapp/rig/configs/api.py
from pathlib import Path
from pyrig.rig.configs.base.json import DictJsonConfigFile
from pyrig.rig.configs.base.base import ConfigDict
class ApiConfigFile(DictJsonConfigFile):
"""Manages api.json configuration."""
def parent_path(self) -> Path:
"""Place in config/ directory."""
return Path("config")
def _configs(self) -> ConfigDict:
"""Define API configuration."""
return {
"api": {
"base_url": "https://api.example.com",
"version": "v1",
"timeout": 30,
"retry": {
"max_attempts": 3,
"backoff_factor": 2,
},
},
"endpoints": [
{
"name": "users",
"path": "/users",
"methods": ["GET", "POST"],
},
{
"name": "posts",
"path": "/posts",
"methods": ["GET", "POST", "PUT", "DELETE"],
},
],
}
Generates config/api.json:
{
"api": {
"base_url": "https://api.example.com",
"version": "v1",
"timeout": 30,
"retry": {
"max_attempts": 3,
"backoff_factor": 2
}
},
"endpoints": [
{
"name": "users",
"path": "/users",
"methods": ["GET", "POST"]
},
{
"name": "posts",
"path": "/posts",
"methods": ["GET", "POST", "PUT", "DELETE"]
}
]
}
Python Configuration Example
Custom CLI Subcommand
# myapp/rig/configs/python/deploy_cmd.py
from pathlib import Path
from pyrig.rig.configs.base.python import PythonConfigFile
class DeployCmdConfigFile(PythonConfigFile):
"""Generates deploy.py CLI subcommand."""
def parent_path(self) -> Path:
"""Place in myapp/rig/cli/ directory."""
from pyrig.rig.tools.package_manager import PackageManager
return Path(PackageManager.I.package_name()) / "rig" / "cli"
def _configs(self) -> str:
"""Generate deploy command source."""
return '''
"""Deploy command for production deployment."""
import typer
app = typer.Typer()
@app.command()
def deploy(
environment: str = typer.Option("production", help="Target environment"),
dry_run: bool = typer.Option(False, help="Run in dry-run mode"),
) -> None:
"""Deploy application to target environment.
Args:
environment: Target environment (production, staging, dev).
dry_run: If True, simulate deployment without making changes.
"""
if dry_run:
typer.echo(f"Dry-run: Would deploy to {environment}")
else:
typer.echo(f"Deploying to {environment}...")
# Deployment logic here
typer.echo("Deployment complete!")
'''
Markdown Configuration Example
Custom Documentation Page
# myapp/rig/configs/markdown/docs/architecture.py
from pathlib import Path
from pyrig.rig.configs.base.markdown import MarkdownConfigFile
class ArchitectureDocConfigFile(MarkdownConfigFile):
"""Generates docs/architecture.md."""
def parent_path(self) -> Path:
"""Place in docs/ directory."""
return Path("docs")
def filename(self) -> str:
"""Use architecture.md."""
return "architecture"
def _configs(self) -> str:
"""Generate architecture documentation."""
from pyrig.rig.tools.package_manager import PackageManager
project_name = PackageManager.I.project_name()
return f'''
# Architecture
Overview of {project_name} architecture.
## Components
### Core
Core application logic.
### API
REST API endpoints.
### Database
PostgreSQL database layer.
## Design Patterns
- **Repository Pattern**: Data access abstraction
- **Service Layer**: Business logic separation
- **Dependency Injection**: Loose coupling
'''
Advanced: Priority-Based Validation
Set higher priority for configs that others depend on:
from pyrig.rig.configs.base.toml import TomlConfigFile
from pyrig.rig.configs.base.base import ConfigDict, Priority
class CoreConfigFile(TomlConfigFile):
"""Core configuration that others read from."""
def priority(self) -> float:
"""Validate early (higher priority)."""
return Priority.HIGH # 30
def parent_path(self) -> Path:
return Path()
def _configs(self) -> ConfigDict:
return {
"core": {
"app_name": "myapp",
"version": "1.0.0",
}
}
class DependentConfigFile(TomlConfigFile):
"""Configuration that reads from CoreConfigFile."""
def priority(self) -> float:
"""Validate after CoreConfigFile."""
return Priority.DEFAULT # 0
def parent_path(self) -> Path:
return Path()
def _configs(self) -> ConfigDict:
"""Read from CoreConfigFile."""
core_config = CoreConfigFile.I.load()
app_name = core_config["core"]["app_name"]
return {
"dependent": {
"parent_app": app_name,
"setting": "value",
}
}
Advanced: Dynamic Configuration
Generate configuration based on project state:
from pathlib import Path
from pyrig.rig.configs.base.toml import TomlConfigFile
from pyrig.rig.configs.base.base import ConfigDict
from pyrig.rig.configs.pyproject import PyprojectConfigFile
class DynamicConfigFile(TomlConfigFile):
"""Configuration that adapts to project settings."""
def parent_path(self) -> Path:
return Path()
def _configs(self) -> ConfigDict:
"""Generate config based on pyproject.toml."""
pyproject = PyprojectConfigFile.I.load()
project_name = pyproject["project"]["name"]
python_version = pyproject["project"]["requires-python"]
return {
"dynamic": {
"project": project_name,
"python": python_version,
"features": self.detect_features(),
}
}
def detect_features(self) -> list[str]:
"""Detect enabled features from dependencies."""
pyproject = PyprojectConfigFile.I.load()
deps = pyproject["project"].get("dependencies", [])
features = []
if any("fastapi" in dep for dep in deps):
features.append("web")
if any("sqlalchemy" in dep for dep in deps):
features.append("database")
if any("pytest" in dep for dep in deps):
features.append("testing")
return features
Override Existing Configuration
Replace pyrig’s default configuration with your own:
# myapp/rig/configs/pyproject.py
from pyrig.rig.configs.pyproject import PyprojectConfigFile as BasePyproject
from pyrig.rig.configs.base.base import ConfigDict
class PyprojectConfigFile(BasePyproject):
"""Custom pyproject.toml with extra settings."""
def _configs(self) -> ConfigDict:
"""Extend base configuration."""
config = super()._configs()
# Add keywords
config["project"]["keywords"] = [
"cli",
"automation",
"devops",
]
# Add custom tool config
config["tool"]["myapp"] = {
"setting1": "value1",
"setting2": "value2",
}
return config
Because this class has the same name as the base class and is in the same relative location (rig.configs.pyproject), it overrides the default configuration.
Best Practices
Structure
-
Organize by purpose: Group related configs in subdirectories
myapp/rig/configs/database/ - Database configurations
myapp/rig/configs/api/ - API configurations
myapp/rig/configs/workflows/ - Custom workflows
-
Use descriptive names: Name classes after their purpose
DatabaseConfigFile not DbCfg
ApiConfigFile not ApiCfg
-
Document behavior: Add docstrings explaining what the config does
Configuration
- Provide sensible defaults: Users should be able to use the defaults
- Support customization: Allow users to add their own settings
- Validate input: Raise clear errors for invalid configuration
- Use environment variables: For sensitive or environment-specific settings
Priority
-
Use HIGH priority (30) for:
- Configs that define core project settings
- Configs that other configs read from
-
Use MEDIUM priority (20) for:
- Configs that need early creation
- Configs similar to pyproject.toml
-
Use DEFAULT priority (0) for:
- Most custom configurations
- Configs that depend on others
Testing Custom Configs
# tests/test_custom_config.py
import pytest
from pathlib import Path
from myapp.rig.configs.myconfig import MyConfigFile
def test_config_generation():
"""Test configuration is generated correctly."""
config = MyConfigFile.I.configs()
assert "app" in config
assert config["app"]["name"] == "myapp"
assert config["app"]["version"] == "1.0.0"
def test_config_file_location():
"""Test file is created in correct location."""
path = MyConfigFile.I.path()
assert path == Path("myconfig.toml")
def test_config_validation():
"""Test validation creates file if missing."""
config_file = MyConfigFile.I
# This would create the file if it doesn't exist
config_file.validate()
assert config_file.path().exists()