# Confumo

**Confumo** derives from "Confuse more".  It's a thin python module that acts like a framework to command‑line flags, environment
variables, and YAML/JSON files into a **single, "deep‑merged" configuration
object**.  The object is always a singleton (one per *application name*) and
can be exposed at module scope so existing code keeps working.

*One line to register your flags, one line to expose the config to the rest of
    your project.*


---

## Highlights

| ✨ | Description |
|---|-------------|
| **Merged sources** | argparse • ENV vars • YAML/JSON • defaults |
| **Singleton registry** | Only *one* `ArgumentParser` run per `app_name` |
| **Auto flag inheritance** | Base class + mix‑ins + leaf class all show up in `--help` |
| **Lazy “print‑once” help** | Libraries defer `--help`; root application prints merged help |
| **Module‑level proxy** | `from my_app.config import log_level` still works |
| **Deep‑merge** | Powered by&nbsp;[confuse](https://github.com/beetbox/confuse) |
| **Cross‑platform paths** | Uses&nbsp;[platformdirs](https://github.com/platformdirs/platformdirs) |

---

## Installation

```bash
pip install confumo
```

---

## 1 · Define a config class

```python
# ui_layer/config.py
import argparse
from confumo import Confumo

class UIConfig(Confumo):
    def add_args(self, p: argparse.ArgumentParser):
        p.add_argument('--theme',   choices=['light', 'dark', 'auto'], default='auto')
        p.add_argument('--font',    default='Roboto')

    def _init_subclass(self):
        self.theme = self.args.theme                 # CLI / env / YAML
        self.font  = self.cfg['font'].get(str)

    def to_dict(self):
        return {'theme': self.theme, 'font': self.font}
```

---

## 2 · Expose it (library mode)

`UIConfig` is meant to be reused by many apps, so the library defers `--help`
printing:

```python
# ui_layer/config.py  (bottom of file)
config = Confumo.expose(
    'ui_layer',          # app_name
    UIConfig,            # concrete subclass
    globals(),
    root=False           # ← do *not* print help here
)
```

Now any package can do:

```python
from ui_layer import config
print(config.theme)
```

and the singleton is shared process‑wide.

---

## 3 · Extend it in an application

```python
# cool_app/config.py
import argparse
from confumo import Confumo
from ui_layer.config import UIConfig

class CoolAppConfig(UIConfig):
    def add_args(self, p: argparse.ArgumentParser):
        p.add_argument('--log_level', default='INFO',
                       choices=['DEBUG','INFO','WARNING','ERROR','CRITICAL'])
        p.add_argument('--library_path', default='~/Pictures')

    def _init_subclass(self):
        super()._init_subclass()
        self.log_level    = self.cfg['log_level'].get(str)
        self.library_path = self.cfg['library_path'].get(str)

    def to_dict(self):
        base = super().to_dict()
        base.update({'log_level': self.log_level,
                     'library_path': self.library_path})
        return base

config = Confumo.expose(
    'cool_app',        # same as CLI program name
    CoolAppConfig,
    globals(),
    publish_to=('ui_layer.config',)   # make ui_layer widgets see the same instance
)  # root=True by default → prints merged --help
```

Running the application:

```bash
python cool_app/main.py --help
```
prints **all** flags (`--theme`, `--font`, `--log_level`, `--library_path`, …)
exactly once.

---

## 4 · Access the config

```python
from cool_app import config               # module‑level shortcut
print(config.library_path)

# or, import‑order‑safe lookup
from confumo import Confumo
cfg = Confumo.get('cool_app', CoolAppConfig)
```

Both lines refer to the same singleton.

---


---

## Precedence order

1. **CLI flags** (`--foo bar`)
2. **Environment variables** (`COOL_APP__FOO=bar`)
3. **YAML file** (`-c settings.yml`) or default path
4. Defaults hard‑coded in your subclass

`confuse` handles deep‑merging dicts & lists.

---

## Persist the merged config

```python
path = config.save_yaml()  # ~/.config/cool_app/cool_app_config.yaml (Linux)
```

The file contains the fully merged state for later inspection.

---

## Testing helpers

`config.copy()` returns a shallow clone whose attribute
changes don’t affect the singleton.

---

## License
AGPLv3 (see LICENSE)

Under the AGPLv3, any modified version or derivative work that is used, especially in a networked or distributed environment—by a school, company, or other organization—must also be licensed under the AGPLv3, with its source code made available to users.