Skip to main content

Automatic Pydantic config generation from function signatures with hyperparameters

Project description

hipr

(pronounced "hyper")

CI codecov

Automatic Pydantic config generation from class and function signatures.

Turn any class into a configurable, serializable, validated component—just add @configurable and mark tunable parameters with Hyper[T].

Before & After

Without hipr — manual config classes, validation, and factory methods:

from dataclasses import dataclass

# 1. Define the Config class (Boilerplate)
@dataclass
class OptimizerConfig:
    learning_rate: float = 0.01
    momentum: float = 0.9

    # 2. Write a factory method to create the object
    def make(self) -> "Optimizer":
        return Optimizer(
            learning_rate=self.learning_rate,
            momentum=self.momentum
        )

# 3. Define the actual class
class Optimizer:
    def __init__(self, learning_rate: float, momentum: float):
        self.learning_rate = learning_rate
        self.momentum = momentum

# 4. Repeat for every component...
@dataclass
class ModelConfig:
    optimizer: OptimizerConfig
    hidden_size: int = 128
    
    def make(self) -> "Model":
        return Model(
            optimizer=self.optimizer.make(),
            hidden_size=self.hidden_size
        )

class Model:
    def __init__(self, optimizer: Optimizer, hidden_size: int):
        self.optimizer = optimizer
        self.hidden_size = hidden_size

With hipr — automatic config generation with validation:

from dataclasses import dataclass
from hipr import configurable, Hyper, Gt, Ge, Lt, DEFAULT

@configurable
@dataclass
class Optimizer:
    learning_rate: Hyper[float, Gt[0.0]] = 0.01
    momentum: Hyper[float, Ge[0.0]] = 0.9

@configurable
@dataclass
class Model:
    hidden_size: Hyper[int, Ge[1]] = 128
    dropout: Hyper[float, Ge[0.0], Lt[1.0]] = 0.1
    optimizer: Hyper[Optimizer] = DEFAULT  # Nested config with default

# Automatic validation
config = Model.Config(hidden_size=256, dropout=0.2)
# config = Model.Config(dropout=1.5)  # ValidationError: must be < 1.0

# Serialize/deserialize
json_str = config.model_dump_json()
loaded = Model.Config.model_validate_json(json_str)

# Instantiate
model = config.make()  # Returns Model instance with nested Optimizer

Installation

pip install hipr
# or: uv add hipr

Core Concepts

Classes & Dataclasses (Primary Use Case)

The @configurable decorator generates a .Config class that captures Hyper[T] parameters:

from dataclasses import dataclass
from hipr import configurable, Hyper, Ge

@configurable
@dataclass
class Optimizer:
    learning_rate: Hyper[float] = 0.01
    momentum: Hyper[float, Ge[0.0]] = 0.9

# Direct instantiation still works
opt = Optimizer(learning_rate=0.001)

# Or use Config for validation + serialization
config = Optimizer.Config(learning_rate=0.001)
opt = config.make()  # Returns Optimizer instance

Regular classes work the same way:

@configurable
class Model:
    def __init__(
        self,
        hidden_size: Hyper[int, Ge[1]] = 128,
        dropout: Hyper[float] = 0.1,
    ):
        self.hidden_size = hidden_size
        self.dropout = dropout

config = Model.Config(hidden_size=256)
model = config.make()  # Returns Model instance

Functions (Syntactic Sugar)

Functions work too—under the hood, @configurable treats them as a class with a __call__ method:

@configurable
def train(
    data: list[float],              # Runtime data (not a hyperparameter)
    epochs: Hyper[int, Ge[1]] = 100,
    lr: Hyper[float, Gt[0.0]] = 0.001,
) -> dict:
    return {"trained": True}

Is handled internally as:

@configurable
@dataclass
class train:
    epochs: Hyper[int, Ge[1]] = 100
    lr: Hyper[float, Gt[0.0]] = 0.001

    def __call__(self, data: list[float]) -> dict:
        return {"trained": True}

The Hyper[T] Annotation

Mark tunable parameters with Hyper[T]:

# Simple type
count: Hyper[int] = 10

# With constraints
rate: Hyper[float, Ge[0.0], Le[1.0]] = 0.5
size: Hyper[int, Ge[1], Le[1000]] = 100

# Required parameter (no default)
learning_rate: Hyper[float, Gt[0.0]]  # Must be provided

Available constraints: Ge, Gt, Le, Lt (numeric bounds), MinLen, MaxLen (length), MultipleOf, Pattern (regex). See constraint docstrings for details.

Nested Configurations

Compose configs hierarchically with DEFAULT:

@configurable
@dataclass
class Pipeline:
    model: Hyper[Model] = DEFAULT      # Uses Model's defaults
    optimizer: Hyper[Optimizer] = DEFAULT

# Override nested values
config = Pipeline.Config(
    model=Model.Config(hidden_size=512),
    optimizer=Optimizer.Config(learning_rate=0.001),
)
pipeline = config.make()  # All nested configs are instantiated

After make(), nested fields are instances, not configs:

print(pipeline.model.hidden_size)      # 512
print(pipeline.optimizer.learning_rate)  # 0.001

Literal Types & Enums

Use Literal or Enum for fixed choices:

from typing import Literal
from enum import Enum

class Mode(str, Enum):
    FAST = "fast"
    SLOW = "slow"

@configurable
def process(
    mode: Hyper[Literal["train", "eval"]] = "train",
    priority: Hyper[Mode] = Mode.FAST,
) -> str:
    return f"{mode}, {priority.value}"

Type Checking

Generate .pyi stubs for full IDE support:

# Generate stubs for your source
hipr-generate-stubs src/

# Or specific files
hipr-generate-stubs my_module.py "lib/**/*.py"

Add to your workflow:

# pyproject.toml
[tool.poe.tasks]
stubs = "hipr-generate-stubs src/"

Serialization

Configs are Pydantic models with full serialization support:

config = Model.Config(hidden_size=256)

# To dict/JSON
config.model_dump()
config.model_dump_json()

# From dict/JSON
Model.Config(**some_dict)
Model.Config.model_validate_json(json_string)

Advanced Features

Methods

Instance methods also work (self is passed automatically):

class Analyzer:
    @configurable
    def detect(self, data: list, threshold: Hyper[float] = 3.0) -> list:
        return [x for x in data if x > threshold]

analyzer = Analyzer()
config = analyzer.detect.Config(threshold=2.0)
result = config.make()(analyzer, [1, 2, 3, 4])

Validation & Safety

  • Constraint conflicts detected at decoration time: Hyper[int, Ge[10], Le[5]] → error
  • Invalid regex patterns caught immediately
  • Circular dependencies in nested configs → error
  • Reserved names: model_config is reserved by Pydantic

Thread Safety

@configurable is thread-safe for concurrent config creation and usage.

Comparison

Feature hipr gin-config hydra tyro
Philosophy Config from code Dependency injection YAML-first CLI from types
Error Detection Decoration + Runtime Runtime only Runtime CLI parse time
Type Checking Full (.pyi stubs) None Partial Full
Boilerplate Minimal Minimal Moderate Minimal
Serialization Pydantic native Custom YAML YAML/JSON
Best For Type-safe APIs, ML experiments Google-style DI Complex multi-run CLI tools

Performance

  • Config creation: <100µs (Pydantic validation)
  • make() overhead: <50µs
  • Direct function call: zero overhead

Contributing

Contributions welcome! See CONTRIBUTING.md.

License

MIT License


hipr — Configuration should be effortless.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hipr-0.1.7.tar.gz (70.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hipr-0.1.7-py3-none-any.whl (33.5 kB view details)

Uploaded Python 3

File details

Details for the file hipr-0.1.7.tar.gz.

File metadata

  • Download URL: hipr-0.1.7.tar.gz
  • Upload date:
  • Size: 70.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for hipr-0.1.7.tar.gz
Algorithm Hash digest
SHA256 0ef35b91526af69e4b056a4e707a98d5f569332eee71ba6a2303527773c29bcb
MD5 8c649dc106dd77d07a40790f46bfe989
BLAKE2b-256 275e2f73ca943cc573ca3fd3088d36e71f32b0f74d2eb96144433494c38851d5

See more details on using hashes here.

File details

Details for the file hipr-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: hipr-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 33.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for hipr-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 13be3b0577a5aafbc125f62a9de6ea749d207bde313888d0fa0d5cadc60d2bb6
MD5 28a123e59553be2d377447b705732dec
BLAKE2b-256 2ea467e93a5cbe0931812ae886dbb5062c5af42f02e8646c3efd0a98dfb3c06d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page