Skip to main content

Runch

Refined munch. Provides basic munch functionality (attribute-style access for python dicts) with additional generic typing support and runtime validation.

Also provides a config reader that reads config files into predefined runch models. Say goodbye to config["key"], config.get("key") and runtime errors caused by missing keys!

Installation

pip install runch

If you find any bugs, please submit an issue or a pull request at GitHub.

Usage

Example Config Model Definition:

$ python3 -m runch ./etc/base.yaml
# Generated from base{.example,}.yaml by runch
# Please be aware that `float` fields might be annotated as `int` due to the lack of type info in the config.

from __future__ import annotations

from typing import List

from pydantic import Field
from runch import RunchModel, RunchConfigReader

class BaseConfigModel(RunchModel):
    host: str
    port: str
    user: str
    password: str


base_reader = RunchConfigReader[BaseConfigModel]("base.yaml", config_dir="./etc", config_type="yaml")
base = base_reader.read()

# example:
print(base.config.host, base.config.port)  # with awesome intellicode support & runtime validation!

# uncomment the following line to enable the watch_update feature
# _base_reader.enable_feature("watch_update", {"update_interval": 10})

Model Definition Generator

$ python -m runch <config_path> [config_ext]

Usage:

Usage: python -m runch <config_path> [config_name [config_ext]]
    Generate a model definition from a config file.

    config_path: path to your config file.
    config_name: controls generated variable name and class name.
    config_type: content type of your config file. Default is `yaml`.

    Example:
        python -m runch path/to/my_config.foo
        python -m runch path/to/my_config.foo chat_config
        python -m runch path/to/my_config.foo chat_config yaml

RunchConfigReader Arguments

# Read config from file.                 ↓ square brackets
example_config_reader = RunchConfigReader[ExampleConfig](
                                        # ^^^^^^^^^^^^^ Config model class name
    config_name="config_file_name",     # with file extension, but don't include the ".example" part
    config_dir="config_dir",            # default is os.environ.get("RUNCH_CONFIG_DIR", "./etc")
    config_type="yaml",                 # default is "yaml"
    config_encoding="utf-8",            # default is "utf-8"
    config_logger=getLogger("example"), # default is None
)
example_config = example_config_reader.read()  # Or .read_lazy() for lazy loading

print(example_config.config.db_host)
$ touch example_config_dir/example_config_file.yaml
db_host: localhost
db_port: 5432
db_user: user
db_password: password
db_name: database

Supported File Formats

  • YAML
  • JSON
  • TOML
  • arbitrary file formats with custom parser, specified via the custom_config_parser param of RunchConfigReader.__init__(). The custom parser should be a function that takes a str-type file content as its first argument, and returns a parsed dictionary.

Use Remote Config Source

from httpx import AsyncClient
from runch import (
    RunchModel,
    RunchAsyncCustomConfigReader,
)


class TestConfig(RunchModel):
    status: str
    method: str


async def example_config_loader(config_name: str) -> TestConfig:
    """Load config from a remote source."""

    print(f"Loading config from remote source for {config_name=}...")

    # Simulate a network request to fetch the config.
    async with AsyncClient() as client:
        response = await client.get(
            f"https://dummyjson.com/test",
            headers=headers,
        )
        response.raise_for_status()

    return TestConfig(**response.json())


test_reader = RunchAsyncCustomConfigReader[TestConfig](
    config_name="example",
    config_loader=example_config_loader,
)

async def main():
    test_config = await test_reader.read()
    print(test_config.config.status)

    # read_cached() does not require `await`, but it raises RunchLookupError if there's no prior sucessful call to `read()`
    print(test_config, test_reader.read_cached())

Note that you can raise runch.exceptions.RunchConfigUnchanged inside custom config loaders to prevent unnecessary config reloads / updates.

Other Features

  • configurable auto sync & update.
  • optional lazy load & evaluate. Useful for optional configs that may not exist.
  • configurable example merging for fast local development.
  • read arbitrary file formats from any places (e.g. disk, network, db) with sync reader + custom config parser / async reader + custom config loader.

Auto Update

test_reader.enable_feature("watch_update", {"update_interval": 2})  # update every 2s
test_reader.enable_feature(
    "watch_update",
    {
        "update_interval": 2.5,  # update every 2.5s
        "on_update_error": "ignore"  # ignore or raise. default=ignore
    }
)

Merge Example Yaml for Local Dev

test_reader = RunchConfigReader[TestConfig](
    config_name="test.yaml",
    config_type="yaml",
)
if os.environ.get("SERVE_MODE") != "prod":
    test_reader.enable_feature("merge_example", {})

User-Defined Custom Config Validation Logic

Note that this is different from the custom config loader feature.

from runch import RunchModel, RunchConfigReader

class CustomConfigModel(RunchModel):
    host: str
    port: int

    def __init__(self, *args: Any, **kw: Any):
        super().__init__(*args, **kw)

        if self.port % 2 == 0:
            raise ValueError("Only odd port numbers are accepted")

Logging

import logging
from logging import getLogger
from typing import Any
from runch import (
    RunchModel,
    RunchConfigReader,
    RunchLogLevel,
)

logging.basicConfig(level=RunchLogLevel.INFO)

class RunchLogAdapter:
    def log(self, level: RunchLogLevel, msg: str, /, *, exc_info: BaseException | None = None, **kwargs: Any):
        getLogger("runch").log(level, f'{msg} %s', " ".join(["{k}={v}" for k, v in kwargs.items()]), exc_info=exc_info)

class TestConfig(RunchModel):
    x: int

test_reader = RunchConfigReader[TestConfig](
    config_name="test.yaml", config_dir="runch/test", logger=RunchLogAdapter()
)
test_reader.enable_feature("watch_update", {"update_interval": 1})
test_config = test_reader.read()  # will call RunchLogAdapter().log with level RunchLogLevel.ERROR if config is invalid.

Metadata

Release files for runch 1.11.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for runch 1.11.1
File Size Uploaded
runch-1.11.1.tar.gz 27.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for runch 1.11.1
File Interpreter ABI Platform
runch-1.11.1-py3-none-any.whl Python 3 none any Details

Total release size: 61.0 kB

Release files / runch-1.11.1.tar.gz

Download URL runch-1.11.1.tar.gz
Size 27.3 kB
Tags Source
SHA-256 checksum
How to use checksums
9eefdf4bdf53a5fbfbb38867aa2bb674041806ff95f0fa51b3fd42be9d49d8cc
BLAKE2b-256 checksum
How to use checksums
46e5f5a7cf4d822fc5b25c717465a85391e5241c259af10fbf84856a26ab2cd1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.1.3 CPython/3.12.6 Darwin/23.6.0

Release files / runch-1.11.1-py3-none-any.whl

Download URL runch-1.11.1-py3-none-any.whl
Size 33.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
00e5c1896904577dd33840ea756d168fa609cab333ae8e8410dc8d06b7781177
BLAKE2b-256 checksum
How to use checksums
8eb1c216678ee8d3f9da6a590ee6e7498ee81ec3560a4b575c5dc9b666b6dc7b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.1.3 CPython/3.12.6 Darwin/23.6.0

Release history Release notifications | RSS feed

This release

1.11.1 This release

2 release files

1.11.0

2 release files

1.10.0

2 release files

1.9.4

2 release files

1.9.3

2 release files

1.9.2

2 release files

1.9.1

2 release files

1.9.0

2 release files

1.8.0

2 release files

1.7.1

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.6

1 release file

1.0.5

1 release file

1.0.4

1 release file

1.0.3

1 release file

1.0.2

1 release file

1.0.1

1 release file

1.0.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page