Metadata-Version: 2.4
Name: envhint
Version: 0.1.0
Summary: Typed environment variable loading via class declaration — zero boilerplate
Project-URL: Homepage, https://github.com/SpinnakerSix/envhint
Project-URL: Issues, https://github.com/SpinnakerSix/envhint/issues
Project-URL: Changelog, https://github.com/SpinnakerSix/envhint/blob/main/CHANGELOG.md
License: MIT
License-File: LICENSE
Keywords: config,dotenv,env,environment,settings,typed
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
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: Programming Language :: Python :: 3.13
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: strcoerce>=0.1.0
Description-Content-Type: text/markdown

# envhint

Typed environment variable loading via class declaration. Requires only `strcoerce`.

## The problem

```python
# Scattered across every project:
PORT    = int(os.environ.get("PORT", "8080"))
DEBUG   = bool(os.environ.get("DEBUG", "false"))  # bug — always True
HOSTS   = os.environ.get("ALLOWED_HOSTS", "").split(",")
TIMEOUT = timedelta(minutes=int(os.environ.get("TIMEOUT_MINUTES", "5")))
```

## Install

```
pip install envhint
```

## Usage

```python
from envhint import Env
from datetime import timedelta

class Settings(Env):
    port: int = 8080
    debug: bool = False
    db_url: str                        # required — raises EnvError if absent
    allowed_hosts: list[str] = []
    request_timeout: timedelta = timedelta(seconds=30)

settings = Settings()

print(settings.port)            # 8080  (int, not "8080")
print(settings.debug)           # False (not True like bool("false"))
print(settings.allowed_hosts)   # ["api.example.com", "admin.example.com"]
```

Set via environment:
```
PORT=9000
DEBUG=false
DB_URL=postgresql://localhost/mydb
ALLOWED_HOSTS=api.example.com,admin.example.com
REQUEST_TIMEOUT=2m30s
```

## All errors reported at once

```python
class Settings(Env):
    db_url: str
    secret_key: str
    api_key: str

Settings(_env={})
# EnvError: Environment configuration errors:
#   DB_URL: required but not set
#   SECRET_KEY: required but not set
#   API_KEY: required but not set
```

## Testing without patching os.environ

```python
def test_my_feature():
    settings = Settings(_env={
        "DB_URL": "postgresql://localhost/test",
        "SECRET_KEY": "test-secret",
    })
    assert settings.port == 8080  # default
```

## .env file support

```python
settings = Settings(_env_file=".env")
# Real env vars take priority over .env file values
```

## Supported types

| Annotation | Parsed from |
|-----------|-------------|
| `str` | passthrough |
| `int` | `"42"` → `42` |
| `float` | `"3.14"` → `3.14` |
| `bool` | `"true/false/yes/no/1/0/on/off"` |
| `list[str]` | `"a,b,c"` → `["a", "b", "c"]` |
| `timedelta` | `"5m"`, `"2h30m"`, `"1d"` |
| `Optional[X]` | absent → `None`, present → coerced as `X` |

## License

MIT
