Metadata-Version: 2.4
Name: decouplet
Version: 0.2.3
Summary: Decouple config with support for secrets directory (e.g., Docker secrets)
Author-email: Kulbarakov Bakdoolot <kulbarakovbh@gmail.com>
License: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: python-decouple>=3.8
Dynamic: license-file

# decouplet

A tiny wrapper around [python-decouple](https://pypi.org/project/python-decouple/) that adds first-class support for reading secrets from a directory (e.g. Docker secrets mounted at `/run/secrets/`).

Drop-in replacement for `python-decouple`'s `config` — same API, extra secrets support.

## Installation

```bash
pip install decouplet
```

## Usage

```python
from decouplet import config

# Read a value — checked in secrets dir first, then env vars / .env file
DATABASE_PASSWORD = config("DATABASE_PASSWORD")

# With a default
DEBUG = config("DEBUG", default="False")

# With type casting (inherited from python-decouple)
PORT = config("PORT", default=8000, cast=int)
IS_ENABLED = config("IS_ENABLED", default=False, cast=bool)
```

## Priority order

When looking up a key, `decouplet` checks sources in this order:

1. **Secrets directory** — files in `/run/secrets/` (or the path set via `SECRETS_PATH`)
2. **Environment variables**
3. **.env file** (standard python-decouple behaviour)
4. **Default value** (if provided)

> Secrets always win. This matches the expected behaviour when deploying with Docker Swarm or Kubernetes secrets.

## Secrets directory

Each secret is stored as a plain-text file whose **filename** (uppercased) becomes the key and whose **contents** become the value. Trailing whitespace/newlines are stripped automatically.

```
/run/secrets/
    database_password   →  key: DATABASE_PASSWORD
    api_key             →  key: API_KEY
```

### Changing the secrets path

Set the `SECRETS_PATH` environment variable to point to a different directory:

```bash
SECRETS_PATH=/my/secrets python app.py
```

Or in your `.env` file:

```
SECRETS_PATH=/my/secrets
```

If the directory does not exist, `decouplet` silently skips it and falls back to env vars / `.env`.

## Docker example

```yaml
# docker-compose.yml
services:
  app:
    image: myapp
    secrets:
      - database_password

secrets:
  database_password:
    file: ./secrets/database_password.txt
```

```python
# app.py
from decouplet import config

DB_PASSWORD = config("DATABASE_PASSWORD")  # read from /run/secrets/database_password
```

## Requirements

- Python >= 3.9
- python-decouple >= 3.8

## License

MIT
