Metadata-Version: 2.4
Name: bean-config
Version: 0.2.0
Summary: Minimal config framework
Author-email: numen-0 <numen.0x1dea@gmail.com>
License: MIT License
        
        Copyright (c) `2026` `numen-0`
        
        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.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: homepage, https://github.com/numen-0/bean
Project-URL: repository, https://github.com/numen-0/bean
Project-URL: issues, https://github.com/numen-0/bean/issues
Keywords: bean,bean.config
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Typing :: Typed
Requires-Python: >=3.14
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# bean.config

`bean.config` is a minimal configuration framework for Python.

Define your configuration as a class and populate it from command-line
arguments, environment variables, defaults, overrides, or custom sources.

> Just enough to arrange some beans.

---

## Overview

With `bean.config` you get:

- Type-safe configuration classes.
- Automatic loading from multiple *built-in* or *user-defined* sources.
- Configurable source priority.
- Custom validators and normalizers.

```py
from enum import Enum
from bean import config

class Mode(Enum):
    DEV = "dev"
    PROD = "prod"

@config
class AppConfig:
    DEBUG: bool = False
    MODE: Mode = Mode.DEV
    HOST: str
    PORT: int = 8080

# `AppConfig` is now the loaded configuration instance.
print(config.dump_str(AppConfig))
```

> **Note**:
>
> To quickly inspect the public API:
>
> ```sh
> python -c "
>     import bean.config as bean
>     for v in sorted(dir(bean)):
>         print(f'- {v}')
> "
> ```

## Installation

Requirements:

- Python `3.14+`

Using `pip`:

```sh
pip install --upgrade bean-config
```

Using `curl` (direct download):

```sh
curl -Ls \
    https://raw.githubusercontent.com/numen-0/bean/refs/heads/main/bean-config/src/bean/config.py
```

## API

This is a quick reference for the main `API`.

For full details, see the [source code](/bean-config/src/bean/config.py).

### Loading

```py
cfg = config.load(
    Config,
    argv=["--host", "localhost"],
)

config.load(
    Config,
    env_prefix="APP",
    overrides={
        "HOST": "localhost",
    },
)

config.load(
    Config,
    priorities=("extra", "defaults"), # only load from extra and then defaults
    extra_sources={
        "extra": foo,                 # foo(field) -> value
    },
)
```

Built-in sources:

| source            | description                                              |
|:-----------------:|:---------------------------------------------------------|
| `args`            | Command-line arguments (`argparse`).                     |
| `envs`            | Environment variables.                                   |
| `defaults`        | Default class attributes.                                |
| `overrides`       | Explicit values passed via the `overrides` parameter.    |

### Validators

Validators can `raise` exceptions or return a boolean signaling success:

```
class Config:
    NAME: str
    PORT: int

    @config.validator("PORT")
    def port_is_valid(self, port: int) -> bool:
        return 0 < port <= 65535

    @config.validator("NAME")
    def name_is_valid(self, name: str):
      if name == "":
          raise ValueError("Empty NAME")
```

> **Note**: Validators may return `True`, `False` or `None`.

> **Note**: A validator fails only if it returns `False` or raises an exception.

### Normalizers

Normalizers can be used to finalize the configuration load.

```
class Config:
    NAME: str
    HOST: str

    @config.normalizer("NAME", "HOST")
    def lowercase(self, name: str, host: str) -> tuple[str, str]:
        return name.lower(), host.lower()

    @config.normalizer("NAME", "HOST", series=True)
    def strip(self, value: str) -> str:
        return value.strip()

    @config.normalizer()
    def normalize(self) -> None:
      ...
```

> **Note**:
>
> The return value must match the declared fields
>
> - No fields -> return `None`
> - One field -> return the normalized value
> - Multiple fields -> return a `tuple` with one value per field
> - `series=True` -> the function receives and returns one field at a time

## Notes

Validators and normalizers follow the same definition rules:

- The number of declared fields must match the function parameters.
- They work with instance methods, `@staticmethod`, and `@classmethod`.
- Execution order is determined by `priority`, then by function name.

## License

All the repo falls under the [MIT License](/LICENSE).

