## `chipenv` A simple but flexible environment loader. Supports `.env` file parsing with optional mode suffixes (e.g. `.env.production`, `.env.testing`, etc.), lazy loading, and dynamic access. ### Features * Mode-based config support (`.env.production`, `.env.testing`, etc.) * Access via: * `env["KEY"]` * `env.get("KEY", ...)` * `env.KEY` * Optional type casting (`str`, `bool`, `list`, `dict`, etc.) * Sane default handling * Does **not** mutate `os.environ` --- ### Use cases * Loading `.env` files in mode-aware Python projects * Separating secrets and configs by deployment context * Dynamically reading values like `env.DB_URL`, `env.get("DEBUG", default=False, cast=cast_bool)` * Avoiding `os.environ` pollution --- ### When to use which class * `Environment`: default choice for most apps; new instance per config. * `EnvShared`: use when you want a single shared instance across the process (legacy singleton behavior). * `TestEnv`: use for tests/fixtures when you want to inject a dict and skip file I/O. --- ### Basic Example ```python from chipenv import Environment, Modes env = Environment(env_file_name=".env", mode=Modes.DEVELOPMENT) print(env.get("DEBUG", default=False)) print(env["API_KEY"]) print(env.DB_URL) ``` Given a `.env.development` file: ```env DEBUG=true API_KEY=secret DB_URL=https://example.com ``` --- ### Smart Casts Example ```python from chipenv import Environment, cast env = Environment() print("INT:", env.get("PORT", cast=cast.cast_int)) # -> int print("FLOAT:", env.get("PI", cast=cast.cast_float)) # -> float print("BOOL:", env.get("ENABLED", cast=cast.cast_bool)) # -> bool print("LIST:", env.get("NUMBERS", cast=cast.cast_list)) # -> list[str] print("TUPLE:", env.get("WORDS", cast=cast.cast_tuple)) # -> tuple[str] print("DICT:", env.get("CONFIG", cast=cast.cast_dict)) # -> dict print("NONE_OR_STR:", env.get("OPTIONAL", cast=cast.cast_none_or_str)) # -> None or str ``` ### Singleton Example ```python from chipenv import EnvShared, Modes env_a = EnvShared(env_file_name=".env", mode=Modes.LOCAL) env_b = EnvShared(env_file_name=".env", mode=Modes.LOCAL) print(env_a is env_b) # -> True ``` ### Test/Fixture Example ```python from chipenv import TestEnv env = TestEnv({"DEBUG": "true", "PORT": "8080"}) print(env.get("DEBUG")) # -> "true" print(env["PORT"]) # -> "8080" ``` Example `.env`: ```env PORT=8080 PI=3.1415 ENABLED=yes NUMBERS=1,2,3 WORDS=hello,world,test CONFIG={"timeout": 30, "debug": true} OPTIONAL=null ``` --- ### Common errors and handling Missing key with no default: ```python from chipenv import Environment, EnviromentKeyMissing env = Environment(suppress_file_not_found=True) try: env.get("MISSING_KEY") except EnviromentKeyMissing as exc: print(exc) ``` Missing `.env` file: ```python from chipenv import Environment try: env = Environment(env_file_name=".env", mode="missing") except FileNotFoundError as exc: print(exc) ``` ### Included Cast Helpers Import them via `from chipenv import cast` and use `cast.cast_int(...)`, etc. All built-in cast functions handle common edge cases: | Cast Function | Description | | ------------------ | ------------------------------------------- | | `cast_str` | Ensures string | | `cast_int` | Converts to integer | | `cast_float` | Converts to float | | `cast_bool` | Accepts `1`, `true`, `yes`, `on`, etc. | | `cast_list` | Comma-split list | | `cast_tuple` | Comma-split, converted to tuple | | `cast_dict` | Parses JSON string into dictionary | | `cast_none_or_str` | Returns `None` if value is `null` or `None` | --- ### EnviromentKeyMissing if a key is get from `env.get` and it has no default given it will raise EnviromentKeyMissing(f"Environment variable '{key}' not found.") ---