Metadata-Version: 2.4
Name: configsmith
Version: 1.0.0
Summary: Unified config loader — .env, YAML, TOML, JSON with env overrides, type casting, validation, and fallback chains
Author: prabhay759
License: MIT
Project-URL: Homepage, https://github.com/prabhay759/configsmith
Project-URL: Repository, https://github.com/prabhay759/configsmith
Project-URL: Issues, https://github.com/prabhay759/configsmith/issues
Keywords: config,configuration,env,yaml,toml,json,dotenv,settings
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: yaml
Requires-Dist: pyyaml>=6; extra == "yaml"
Provides-Extra: toml
Requires-Dist: tomli>=2; python_version < "3.11" and extra == "toml"
Provides-Extra: all
Requires-Dist: pyyaml>=6; extra == "all"
Requires-Dist: tomli>=2; python_version < "3.11" and extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pyyaml>=6; extra == "dev"
Requires-Dist: tomli>=2; extra == "dev"
Dynamic: license-file

# configsmith

> Unified config loader for Python — reads `.env`, YAML, TOML, and JSON with environment variable overrides, type casting, schema validation, and fallback chains. One API, every format.

[![PyPI version](https://img.shields.io/pypi/v/configsmith.svg)](https://pypi.org/project/configsmith/)
[![Python](https://img.shields.io/pypi/pyversions/configsmith.svg)](https://pypi.org/project/configsmith/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

---

## Installation

```bash
pip install configsmith          # core (.env, JSON)
pip install configsmith[yaml]    # adds YAML support
pip install configsmith[toml]    # adds TOML support (Python < 3.11)
pip install configsmith[all]     # everything
```

---

## Quick Start

```python
from configsmith import ConfigSmith, Field

config = ConfigSmith(schema={
    "port":    Field(int,  default=8080),
    "debug":   Field(bool, default=False),
    "env":     Field(str,  choices=["dev", "staging", "prod"]),
    "tags":    Field(list, default=[]),
})

config.chain(".env", "config.yaml", "config.local.yaml").load_env().validate()

port  = config.get("port")   # int, env var PORT overrides file value
debug = config.get("debug")  # bool
tags  = config.get("tags")   # list
```

---

## Concepts

### Layers & Fallback Chains

Config is built from ordered layers — later layers override earlier ones:

```python
config = (ConfigSmith()
    .load(".env")                    # base config
    .load("config.yaml")             # app config
    .load("config.local.yaml", optional=True)  # local overrides (git-ignored)
    .load_env()                      # env vars win over everything
)
```

Or use the shorthand:

```python
config.chain(".env", "config.yaml", "config.local.yaml").load_env()
```

### Type Casting

Without schema, all values are raw strings. With `Field`, values are automatically cast:

```python
schema = {
    "port":    Field(int),            # "8080" → 8080
    "debug":   Field(bool),           # "true" → True, "false" → False
    "workers": Field(float),          # "2.5" → 2.5
    "tags":    Field(list),           # "a,b,c" or '["a","b"]' → ["a","b","c"]
    "meta":    Field(dict),           # '{"k": "v"}' → {"k": "v"}
}
```

Bool truthy values: `"1"`, `"true"`, `"yes"`, `"on"` (case-insensitive).

### Schema Validation

```python
schema = {
    "api_key":  Field(str, description="Required Stripe API key"),
    "env":      Field(str, choices=["dev", "staging", "prod"]),
    "port":     Field(int, default=8080, validator=lambda v: 1 <= v <= 65535),
    "workers":  Field(int, default=4),
}

config = ConfigSmith(schema=schema).load("config.json")
config.validate()  # raises ConfigValidationError listing ALL errors at once
```

### Environment Variable Overrides

Environment variables always win. By default, `Field("port")` checks `PORT` env var:

```bash
PORT=9999 python app.py  # overrides any file value
```

Custom env var name:

```python
Field(int, env_var="MY_APP_PORT")  # reads MY_APP_PORT instead of PORT
```

Scoped prefix (only import `APP_*` vars):

```python
ConfigSmith(env_prefix="APP").load_env()
# APP_PORT=8080 becomes key "PORT"
```

---

## API Reference

### `ConfigSmith`

| Method | Description |
|---|---|
| `load(path, optional=False)` | Load a file (auto-detect format) |
| `load_env(prefix="")` | Load env vars as a high-priority layer |
| `load_dict(data)` | Load a plain dict |
| `chain(*paths, optional=True)` | Load multiple files as a fallback chain |
| `validate()` | Validate against schema, raise on errors |
| `get(key, default=None)` | Get a value (case-insensitive) |
| `require(key)` | Get a value, raise if missing |
| `as_dict()` | Return all config as a plain dict |

### `Field`

```python
Field(
    type_=str,          # Target Python type: str, int, float, bool, list, dict
    default=REQUIRED,   # Default value. Omit to make field required.
    description="",     # Shown in validation error messages
    choices=None,       # List of allowed values
    validator=None,     # Callable(value) -> bool
    env_var=None,       # Override env var name (default: KEY.upper())
)
```

### Supported Formats

| Extension | Format | Extra dependency |
|---|---|---|
| `.env` | dotenv | None |
| `.json` | JSON | None |
| `.yaml`, `.yml` | YAML | `pip install pyyaml` |
| `.toml` | TOML | stdlib on Python 3.11+, else `pip install tomli` |

---

## Running Tests

```bash
pip install pytest
pytest tests/ -v
```

---

## License

MIT © prabhay759
