Why Heracless?
Stop wrestling with dictionaries and string keys. Heracless automatically converts your YAML configuration files into Python dataclasses with full type safety and IDE autocomplete support.
Plus it's written in Rust for blazing-fast performance.
config = yaml.load(open("config.yaml"))
db_host = config["database"]["host"]
db_port = config["databse"]["port"]
config = load_config()
db_host = config.database.host
db_port = config.database.port
Features
- Automatic Type Generation - Generates
.pyi stub files for full IDE support
- Type Safety - Catch configuration errors at development time, not runtime
- Zero Boilerplate - No manual dataclass definitions needed
- IDE Autocomplete - Full IntelliSense/autocomplete for all config values
- Immutable by Default - Frozen dataclasses prevent accidental modifications
- Rust-Powered Performance - Native Rust backend for blazing-fast YAML parsing and stub generation
Installation
From PyPI (Recommended)
pip install heracless
From Source
git clone https://github.com/felixscode/heracless.git
cd heracless
pip install -e .
Requirements
| 3.10 - 3.13 | Fully Supported |
| 3.9 and below | Not Supported |
Dependencies: PyYAML, black, art
Note: Prebuilt Rust wheels are available for Linux, macOS, and Windows. No Rust installation required!
Quick Start
1. Create your configuration file
Create a config.yaml file with your settings:
database:
host: localhost
port: 5432
name: myapp_db
credentials:
username: admin
password: secret123
api:
base_url: https://api.example.com
timeout: 30
retries: 3
features:
enable_caching: true
max_cache_size: 1000
2. Set up the config loader
Create a load_config.py file in your project:
from pathlib import Path
from typing import TypeVar
from heracless import load_config as _load_config
CONFIG_YAML_PATH = Path(__file__).parent.parent / "config.yaml"
Config = TypeVar("Config")
def load_config(config_path: Path | str = CONFIG_YAML_PATH,
frozen: bool = True,
stub_dump: bool = True) -> Config:
"""Load configuration and generate type stubs."""
file_path = Path(__file__).resolve() if stub_dump else None
return _load_config(config_path, file_path, frozen=frozen)
3. Use your config with full type safety
from myproject.load_config import load_config
config = load_config()
print(f"Connecting to {config.database.host}:{config.database.port}")
print(f"Database: {config.database.name}")
print(f"API URL: {config.api.base_url}")
print(f"Caching enabled: {config.features.enable_caching}")
Output:
Connecting to localhost:5432
Database: myapp_db
API URL: https://api.example.com
Caching enabled: True
Generated Type Stub Example
After the first run, Heracless automatically generates a load_config.pyi file:
from dataclasses import dataclass
from typing import TypeVar
@dataclass(frozen=True)
class Credentials:
username: str
password: str
@dataclass(frozen=True)
class Database:
host: str
port: int
name: str
credentials: Credentials
@dataclass(frozen=True)
class Api:
base_url: str
timeout: int
retries: int
@dataclass(frozen=True)
class Features:
enable_caching: bool
max_cache_size: int
@dataclass(frozen=True)
class Config:
database: Database
api: Api
features: Features
This stub file enables full IDE autocomplete and type checking
Usage Examples
Basic Configuration Loading
from myproject.load_config import load_config
config = load_config()
db_url = f"{config.database.host}:{config.database.port}"
Mutable Configuration
config = load_config(frozen=False)
config.database.host = "192.168.1.100"
Converting to Dictionary
from heracless.utils.helper import as_dict
config = load_config()
config_dict = as_dict(config)
print(config_dict["database"]["host"])
Creating Config from Dictionary
from heracless.utils.helper import from_dict
config_dict = {
"database": {
"host": "localhost",
"port": 5432
},
"api": {
"base_url": "https://api.example.com",
"timeout": 30
}
}
config = from_dict(config_dict, frozen=True)
print(config.database.host)
Updating Configuration Values
from heracless.utils.helper import mutate_config
config = load_config()
new_config = mutate_config(config, "database.host", "production-db.example.com")
print(config.database.host)
print(new_config.database.host)
CLI Tool Usage
Heracless includes a CLI tool for generating stub files and validating configs:
python -m heracless config.yaml --parse types.pyi
python -m heracless config.yaml --dry
python -m heracless --help
Project Structure Example
Here's a recommended project structure:
my_project/
├── src/
│ └── myproject/
│ ├── __init__.py
│ ├── main.py
│ └── config/
│ ├── __init__.py
│ ├── load_config.py # Your config loader
│ └── load_config.pyi # Auto-generated types
├── config/
│ ├── config.yaml # Main config
│ ├── config.dev.yaml # Development overrides
│ └── config.prod.yaml # Production overrides
├── tests/
│ └── test_config.py
├── pyproject.toml
└── README.md
Troubleshooting
Issue: IDE not showing autocomplete
Solutions:
- Ensure the
.pyi file exists next to your load_config.py
- Reload your IDE/editor window
- Check that your language server is running (VSCode: check Python extension)
- For PyCharm: File → Invalidate Caches → Restart
Issue: TypeError: 'Config' object is immutable
Solution: This is by design (frozen dataclass). To modify configs:
- Use
mutate_config() helper to create updated copies
- Or load with
frozen=False for mutable configs (not recommended)
Issue: YAML parsing errors
Solution: Ensure your YAML is valid:
python -c "import yaml; yaml.safe_load(open('config.yaml'))"
API Reference
Core Functions
load_config(config_path, file_path, frozen)
Load a YAML configuration file and convert it to a typed dataclass.
Parameters:
config_path (Path | str): Path to the YAML configuration file
file_path (Path | str | None): Path where stub file should be generated (None to skip)
frozen (bool): Whether the resulting dataclass should be immutable (default: True)
Returns: Config dataclass with attributes matching your YAML structure
Raises:
FileNotFoundError: If config file doesn't exist
yaml.YAMLError: If YAML file is malformed
Helper Functions
mutate_config(config, name, value)
Create a new config with an updated value (immutable pattern).
from heracless.utils.helper import mutate_config
config = load_config()
new_config = mutate_config(config, "database.port", 3306)
as_dict(config)
Convert a Config dataclass to a nested dictionary.
from heracless.utils.helper import as_dict
config = load_config()
config_dict = as_dict(config)
from_dict(config_dict, frozen)
Create a Config dataclass from a dictionary.
from heracless.utils.helper import from_dict
config_dict = {"database": {"host": "localhost"}}
config = from_dict(config_dict, frozen=True)
Contributing
Contributions are welcome! Here's how you can help:
Development Setup
git clone https://github.com/felixscode/heracless.git
cd heracless
pip install -e .[dev]
pytest
mypy heracless
black heracless tests
Running Tests
pytest
pytest --cov=heracless --cov-report=html
pytest tests/test_config.py
Development Dependencies
Install development dependencies with:
pip install -e .[dev]
This includes:
pytest - Testing framework
pytest-cov - Coverage reporting
mypy - Static type checking
types-PyYAML - Type stubs for PyYAML
Documentation Development
To work on the documentation:
pip install -e .[doc]
mkdocs serve
mkdocs build
Reporting Issues
Found a bug or have a feature request? Open an issue on GitHub.
Please include:
- Heracless version (
pip show heracless)
- Python version
- Operating system
- Minimal reproducible example
- Expected vs actual behavior
Roadmap
Current Version: 0.5.1
Planned Features
License
Heracless is released under the MIT License. See LICENSE file for details.
MIT License
Copyright (c) 2023 Felix Schelling
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
TL;DR: You can freely use, modify, and distribute this software, even for commercial purposes.
Links & Resources
Author
Felix Schelling
If Heracless helps your project, consider giving it a star on GitHub!
Back to Top