envbool 0.1.1__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
envbool-0.1.2/PKG-INFO ADDED
@@ -0,0 +1,358 @@
1
+ Metadata-Version: 2.4
2
+ Name: envbool
3
+ Version: 0.1.2
4
+ Summary: A small Python library and CLI tool for coercing environment variables (and arbitrary strings) into boolean values.
5
+ Keywords: environment variables,boolean,configuration,env,coerce
6
+ Author: Kyle O'Malley
7
+ Author-email: Kyle O'Malley <j.kyle.omalley@gmail.com>
8
+ License-Expression: MIT
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Classifier: Topic :: Utilities
13
+ Classifier: Environment :: Console
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Typing :: Typed
21
+ Requires-Dist: platformdirs>=4.9.6
22
+ Requires-Python: >=3.11
23
+ Project-URL: Homepage, https://github.com/jkomalley/envbool
24
+ Project-URL: Repository, https://github.com/jkomalley/envbool
25
+ Project-URL: Issues, https://github.com/jkomalley/envbool/issues
26
+ Project-URL: Changelog, https://github.com/jkomalley/envbool/releases
27
+ Description-Content-Type: text/markdown
28
+
29
+ <div align="center">
30
+
31
+ # envbool
32
+
33
+ **Coerce environment variables and strings into booleans — sensibly.**
34
+
35
+ [![PyPI version](https://img.shields.io/pypi/v/envbool)](https://pypi.org/project/envbool/)
36
+ [![Python versions](https://img.shields.io/pypi/pyversions/envbool)](https://pypi.org/project/envbool/)
37
+ [![License: MIT](https://img.shields.io/github/license/jkomalley/envbool)](LICENSE)
38
+ [![CI](https://github.com/jkomalley/envbool/actions/workflows/ci.yml/badge.svg)](https://github.com/jkomalley/envbool/actions/workflows/ci.yml)
39
+
40
+ </div>
41
+
42
+ ---
43
+
44
+ Reading a boolean out of the environment is the kind of thing every project
45
+ reinvents, slightly differently, in slightly buggy ways:
46
+
47
+ ```python
48
+ DEBUG = os.environ.get("DEBUG", "").lower() in ("1", "true", "yes")
49
+ VERBOSE = os.environ.get("VERBOSE", "").lower() in ("1", "true", "yes")
50
+ CACHE = os.environ.get("CACHE", "").lower() in ("1", "true", "yes")
51
+ ```
52
+
53
+ `envbool` is that snippet, done once and done properly:
54
+
55
+ ```python
56
+ from envbool import envbool
57
+
58
+ DEBUG = envbool("DEBUG")
59
+ VERBOSE = envbool("VERBOSE")
60
+ CACHE = envbool("CACHE")
61
+ ```
62
+
63
+ ## Features
64
+
65
+ - **Lenient by default, strict when you want it.** Unrecognized values quietly
66
+ become `False`, or raise on demand to catch typos in production config.
67
+ - **Always returns `bool`.** No `None`, no surprises in your type signatures.
68
+ - **Customizable value sets.** Replace or extend the truthy/falsy words your
69
+ environment uses.
70
+ - **Config files.** Share defaults across a project via `envbool.toml` or
71
+ `[tool.envbool]` in `pyproject.toml`.
72
+ - **A CLI for shell scripts.** Exit codes map to truthiness, so it drops
73
+ straight into `&&` / `||` chains.
74
+ - **Zero ceremony.** One dependency, fully typed, Python 3.11+.
75
+
76
+ ## Contents
77
+
78
+ - [Installation](#installation)
79
+ - [Usage](#usage)
80
+ - [Command-line interface](#command-line-interface)
81
+ - [Configuration](#configuration)
82
+ - [API reference](#api-reference)
83
+ - [Advanced topics](#advanced-topics)
84
+ - [Contributing](#contributing)
85
+ - [License](#license)
86
+
87
+ ## Installation
88
+
89
+ ```bash
90
+ pip install envbool
91
+ # or
92
+ uv add envbool
93
+ ```
94
+
95
+ ## Usage
96
+
97
+ ### The basics
98
+
99
+ `envbool` is **lenient by default**: anything not recognized as truthy returns
100
+ `False`, and unset or empty variables return the default.
101
+
102
+ ```python
103
+ from envbool import envbool
104
+
105
+ DEBUG = envbool("DEBUG") # False if unset or empty
106
+ CACHE = envbool("CACHE", default=True) # True if unset or empty
107
+ ```
108
+
109
+ The built-in truthy values are `true`, `1`, `yes`, `on`; the falsy values are
110
+ `false`, `0`, `no`, `off`. Comparison is case-insensitive and ignores
111
+ surrounding whitespace.
112
+
113
+ ### Strict mode
114
+
115
+ Pass `strict=True` to raise `InvalidBoolValueError` on anything outside the
116
+ truthy/falsy sets — ideal for failing fast on a misconfigured deployment.
117
+
118
+ ```python
119
+ import sys
120
+ from envbool import envbool, InvalidBoolValueError
121
+
122
+ try:
123
+ USE_SSL = envbool("USE_SSL", strict=True)
124
+ except InvalidBoolValueError as e:
125
+ sys.exit(f"Bad value for USE_SSL: {e.value!r}")
126
+ ```
127
+
128
+ ### Custom value sets
129
+
130
+ When your environment speaks a different dialect, **extend** the defaults or
131
+ **replace** them outright:
132
+
133
+ ```python
134
+ # Add to the built-in sets
135
+ FEATURE = envbool("FEATURE_FLAG", extend_truthy={"enabled", "y"})
136
+
137
+ # Replace them entirely
138
+ LOCALE = envbool("USE_METRIC", truthy={"metric"}, falsy={"imperial"})
139
+ ```
140
+
141
+ ### Coercing arbitrary strings
142
+
143
+ Use `to_bool` for values that don't come from the environment. It accepts the
144
+ same keyword arguments as `envbool`.
145
+
146
+ ```python
147
+ from envbool import to_bool
148
+
149
+ to_bool("yes") # True
150
+ to_bool("0") # False
151
+ to_bool("maybe", strict=True) # raises InvalidBoolValueError
152
+ ```
153
+
154
+ ## Command-line interface
155
+
156
+ The `envbool` command exits `0` for truthy, `1` for falsy, and `2` on error, so
157
+ it composes naturally with shell control flow.
158
+
159
+ ```console
160
+ $ export DEBUG=true
161
+ $ envbool DEBUG && echo "debug is on"
162
+ debug is on
163
+
164
+ $ echo "Verbose: $(envbool --print VERBOSE)"
165
+ Verbose: false
166
+
167
+ $ echo "yes" | envbool && echo "truthy"
168
+ truthy
169
+
170
+ $ envbool --strict ENABLE_CACHE || echo "cache is off or misconfigured"
171
+ cache is off or misconfigured
172
+ ```
173
+
174
+ Input is taken from a `VAR_NAME` argument, the `--value` flag, or a stdin pipe —
175
+ in that order of priority.
176
+
177
+ ```console
178
+ $ envbool --help
179
+ usage: envbool [-h] [--value TEXT] [--strict] [--warn] [--default] [--print]
180
+ [--truthy VALUE] [--falsy VALUE] [--extend-truthy VALUE]
181
+ [--extend-falsy VALUE] [--show-config]
182
+ [VAR_NAME]
183
+
184
+ Coerce an environment variable or string to a boolean.
185
+
186
+ positional arguments:
187
+ VAR_NAME Environment variable name to check.
188
+
189
+ options:
190
+ -h, --help show this help message and exit
191
+ --value, -v TEXT Check a literal string instead of an env var.
192
+ --strict, -s Raise error on unrecognized values.
193
+ --warn Log a warning on unrecognized values.
194
+ --default, -d Default value if unset/empty (default: false).
195
+ --print, -p Print "true" or "false" instead of using exit codes.
196
+ --truthy VALUE Replace the truthy set with VALUE (repeatable).
197
+ --falsy VALUE Replace the falsy set with VALUE (repeatable).
198
+ --extend-truthy VALUE
199
+ Add VALUE to the truthy set (repeatable).
200
+ --extend-falsy VALUE Add VALUE to the falsy set (repeatable).
201
+ --show-config Print the effective configuration and exit.
202
+ ```
203
+
204
+ A few rules worth knowing:
205
+
206
+ - Omitting `--strict` / `--warn` defers to the config file setting.
207
+ - `VAR_NAME` and `--value` are mutually exclusive.
208
+ - `--show-config` prints the effective configuration and exits. It cannot be
209
+ combined with `VAR_NAME`, `--value`, `--print`, or `--default`, but it *can*
210
+ take the value-set flags to preview overrides.
211
+ - With no `VAR_NAME`, `--value`, or piped stdin, the CLI prints usage and exits `2`.
212
+
213
+ ## Configuration
214
+
215
+ Share defaults across a project by dropping an `envbool.toml` at its root (or a
216
+ `[tool.envbool]` table in `pyproject.toml`):
217
+
218
+ ```toml
219
+ # envbool.toml
220
+ strict = true
221
+ extend_truthy = ["enabled"]
222
+ extend_falsy = ["disabled"]
223
+ ```
224
+
225
+ `envbool` walks up from the current directory to find the nearest project config,
226
+ then falls back to a user-level `config.toml` in the platform's standard config
227
+ directory (`~/.config/envbool/` on Linux, `~/Library/Application Support/envbool/`
228
+ on macOS), resolved via [platformdirs](https://pypi.org/project/platformdirs/).
229
+
230
+ Values resolve in three layers, each overriding the last:
231
+
232
+ ```
233
+ built-in defaults → config file → function arguments / CLI flags
234
+ ```
235
+
236
+ Set `ENVBOOL_NO_CONFIG=1` to skip config discovery entirely.
237
+
238
+ ## API reference
239
+
240
+ | Symbol | Description |
241
+ | --- | --- |
242
+ | `envbool(var, **opts)` | Read an environment variable and return `bool`. |
243
+ | `to_bool(value, **opts)` | Coerce a string to `bool`. |
244
+ | `load_config()` | Load and return the active `EnvBoolConfig` (cached). |
245
+ | `EnvBoolConfig` | Frozen dataclass: `strict`, `warn`, `effective_truthy`, `effective_falsy`, `source_path`. |
246
+ | `DEFAULT_TRUTHY` | `frozenset` of the built-in truthy strings. |
247
+ | `DEFAULT_FALSY` | `frozenset` of the built-in falsy strings. |
248
+ | `EnvBoolError` | Base class for every exception the library raises. |
249
+ | `InvalidBoolValueError` | Raised in strict mode for unrecognized values. Also a `ValueError`. |
250
+ | `ConfigError` | Raised when a config file is malformed. |
251
+
252
+ `envbool()` and `to_bool()` share the same keyword-only options:
253
+
254
+ | Option | Type | Default | Meaning |
255
+ | --- | --- | --- | --- |
256
+ | `default` | `bool` | `False` | Returned for unset/empty input. |
257
+ | `strict` | `bool \| None` | `None` | Raise on unrecognized values (`None` defers to config). |
258
+ | `warn` | `bool \| None` | `None` | Log a warning on unrecognized values (`None` defers to config). |
259
+ | `truthy` / `falsy` | `Iterable[str] \| None` | `None` | **Replace** the effective set. |
260
+ | `extend_truthy` / `extend_falsy` | `Iterable[str] \| None` | `None` | **Extend** the effective set. |
261
+
262
+ ## Advanced topics
263
+
264
+ ### Exception handling
265
+
266
+ Every exception inherits from `EnvBoolError`, so a single `except EnvBoolError`
267
+ catches the whole library. Catch a specific subclass when you need its detail:
268
+
269
+ ```python
270
+ from envbool import envbool, InvalidBoolValueError
271
+
272
+ try:
273
+ result = envbool("MY_VAR", strict=True)
274
+ except InvalidBoolValueError as e:
275
+ print(e.var) # "MY_VAR" — env var name, or None when raised from to_bool()
276
+ print(e.value) # "maybe" — the normalized (stripped, lowercased) value
277
+ print(e.truthy) # frozenset({"true", "1", "yes", "on"}) — effective truthy set
278
+ print(e.falsy) # frozenset({"false", "0", "no", "off"}) — effective falsy set
279
+ ```
280
+
281
+ `InvalidBoolValueError` also subclasses the built-in `ValueError`, so existing
282
+ `except ValueError` handlers keep working. Its message spells out exactly what
283
+ was expected:
284
+
285
+ ```
286
+ InvalidBoolValueError: Invalid boolean value for MY_VAR: 'maybe'
287
+ Expected truthy: 1, on, true, yes
288
+ Expected falsy: 0, false, no, off
289
+ ```
290
+
291
+ `ConfigError` is raised when a config file is found but malformed (for example,
292
+ `strict = "yes"` instead of `strict = true`). It carries the offending path on
293
+ `e.path`.
294
+
295
+ ### Logging
296
+
297
+ `envbool` logs through the standard `logging` module under the `"envbool"`
298
+ namespace and attaches no handlers of its own — configure it like any other
299
+ library logger:
300
+
301
+ ```python
302
+ import logging
303
+
304
+ logging.getLogger("envbool").setLevel(logging.DEBUG)
305
+ logging.getLogger("envbool").addHandler(logging.StreamHandler())
306
+ ```
307
+
308
+ | Level | When |
309
+ | --- | --- |
310
+ | `DEBUG` | A config file was discovered and loaded, or none was found. |
311
+ | `WARNING` | An unrecognized value fell through in lenient mode (only when `warn=True`). |
312
+ | `WARNING` | The truthy and falsy sets overlap (truthy wins). |
313
+
314
+ ### The unset-vs-empty distinction
315
+
316
+ `envbool()` always returns `bool` and deliberately cannot tell an unset variable
317
+ apart from one set to the empty string — both yield `default`. Most deployment
318
+ tooling can't distinguish the two either, and a plain `bool` keeps call sites
319
+ clean. When you genuinely need the distinction, check `os.environ` yourself:
320
+
321
+ ```python
322
+ import os
323
+ from envbool import envbool
324
+
325
+ if "MY_VAR" not in os.environ:
326
+ ... # truly unset — handle the "not configured" case
327
+ else:
328
+ result = envbool("MY_VAR")
329
+ ```
330
+
331
+ ### Testing code that uses envbool
332
+
333
+ `envbool` loads its config file once and caches it for the process lifetime. If
334
+ your tests create temporary config files, clear that cache between them with an
335
+ autouse fixture:
336
+
337
+ ```python
338
+ # conftest.py
339
+ import pytest
340
+ from envbool._config import _reset_config
341
+
342
+ @pytest.fixture(autouse=True)
343
+ def _reset_envbool_config():
344
+ yield
345
+ _reset_config()
346
+ ```
347
+
348
+ `_reset_config()` is private but stable and exists for exactly this purpose; it
349
+ clears the cache under a lock, so it is safe to call from any thread.
350
+
351
+ ## Contributing
352
+
353
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for development
354
+ setup, project layout, and the conventions this repo follows.
355
+
356
+ ## License
357
+
358
+ Released under the [MIT License](LICENSE).
@@ -0,0 +1,330 @@
1
+ <div align="center">
2
+
3
+ # envbool
4
+
5
+ **Coerce environment variables and strings into booleans — sensibly.**
6
+
7
+ [![PyPI version](https://img.shields.io/pypi/v/envbool)](https://pypi.org/project/envbool/)
8
+ [![Python versions](https://img.shields.io/pypi/pyversions/envbool)](https://pypi.org/project/envbool/)
9
+ [![License: MIT](https://img.shields.io/github/license/jkomalley/envbool)](LICENSE)
10
+ [![CI](https://github.com/jkomalley/envbool/actions/workflows/ci.yml/badge.svg)](https://github.com/jkomalley/envbool/actions/workflows/ci.yml)
11
+
12
+ </div>
13
+
14
+ ---
15
+
16
+ Reading a boolean out of the environment is the kind of thing every project
17
+ reinvents, slightly differently, in slightly buggy ways:
18
+
19
+ ```python
20
+ DEBUG = os.environ.get("DEBUG", "").lower() in ("1", "true", "yes")
21
+ VERBOSE = os.environ.get("VERBOSE", "").lower() in ("1", "true", "yes")
22
+ CACHE = os.environ.get("CACHE", "").lower() in ("1", "true", "yes")
23
+ ```
24
+
25
+ `envbool` is that snippet, done once and done properly:
26
+
27
+ ```python
28
+ from envbool import envbool
29
+
30
+ DEBUG = envbool("DEBUG")
31
+ VERBOSE = envbool("VERBOSE")
32
+ CACHE = envbool("CACHE")
33
+ ```
34
+
35
+ ## Features
36
+
37
+ - **Lenient by default, strict when you want it.** Unrecognized values quietly
38
+ become `False`, or raise on demand to catch typos in production config.
39
+ - **Always returns `bool`.** No `None`, no surprises in your type signatures.
40
+ - **Customizable value sets.** Replace or extend the truthy/falsy words your
41
+ environment uses.
42
+ - **Config files.** Share defaults across a project via `envbool.toml` or
43
+ `[tool.envbool]` in `pyproject.toml`.
44
+ - **A CLI for shell scripts.** Exit codes map to truthiness, so it drops
45
+ straight into `&&` / `||` chains.
46
+ - **Zero ceremony.** One dependency, fully typed, Python 3.11+.
47
+
48
+ ## Contents
49
+
50
+ - [Installation](#installation)
51
+ - [Usage](#usage)
52
+ - [Command-line interface](#command-line-interface)
53
+ - [Configuration](#configuration)
54
+ - [API reference](#api-reference)
55
+ - [Advanced topics](#advanced-topics)
56
+ - [Contributing](#contributing)
57
+ - [License](#license)
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ pip install envbool
63
+ # or
64
+ uv add envbool
65
+ ```
66
+
67
+ ## Usage
68
+
69
+ ### The basics
70
+
71
+ `envbool` is **lenient by default**: anything not recognized as truthy returns
72
+ `False`, and unset or empty variables return the default.
73
+
74
+ ```python
75
+ from envbool import envbool
76
+
77
+ DEBUG = envbool("DEBUG") # False if unset or empty
78
+ CACHE = envbool("CACHE", default=True) # True if unset or empty
79
+ ```
80
+
81
+ The built-in truthy values are `true`, `1`, `yes`, `on`; the falsy values are
82
+ `false`, `0`, `no`, `off`. Comparison is case-insensitive and ignores
83
+ surrounding whitespace.
84
+
85
+ ### Strict mode
86
+
87
+ Pass `strict=True` to raise `InvalidBoolValueError` on anything outside the
88
+ truthy/falsy sets — ideal for failing fast on a misconfigured deployment.
89
+
90
+ ```python
91
+ import sys
92
+ from envbool import envbool, InvalidBoolValueError
93
+
94
+ try:
95
+ USE_SSL = envbool("USE_SSL", strict=True)
96
+ except InvalidBoolValueError as e:
97
+ sys.exit(f"Bad value for USE_SSL: {e.value!r}")
98
+ ```
99
+
100
+ ### Custom value sets
101
+
102
+ When your environment speaks a different dialect, **extend** the defaults or
103
+ **replace** them outright:
104
+
105
+ ```python
106
+ # Add to the built-in sets
107
+ FEATURE = envbool("FEATURE_FLAG", extend_truthy={"enabled", "y"})
108
+
109
+ # Replace them entirely
110
+ LOCALE = envbool("USE_METRIC", truthy={"metric"}, falsy={"imperial"})
111
+ ```
112
+
113
+ ### Coercing arbitrary strings
114
+
115
+ Use `to_bool` for values that don't come from the environment. It accepts the
116
+ same keyword arguments as `envbool`.
117
+
118
+ ```python
119
+ from envbool import to_bool
120
+
121
+ to_bool("yes") # True
122
+ to_bool("0") # False
123
+ to_bool("maybe", strict=True) # raises InvalidBoolValueError
124
+ ```
125
+
126
+ ## Command-line interface
127
+
128
+ The `envbool` command exits `0` for truthy, `1` for falsy, and `2` on error, so
129
+ it composes naturally with shell control flow.
130
+
131
+ ```console
132
+ $ export DEBUG=true
133
+ $ envbool DEBUG && echo "debug is on"
134
+ debug is on
135
+
136
+ $ echo "Verbose: $(envbool --print VERBOSE)"
137
+ Verbose: false
138
+
139
+ $ echo "yes" | envbool && echo "truthy"
140
+ truthy
141
+
142
+ $ envbool --strict ENABLE_CACHE || echo "cache is off or misconfigured"
143
+ cache is off or misconfigured
144
+ ```
145
+
146
+ Input is taken from a `VAR_NAME` argument, the `--value` flag, or a stdin pipe —
147
+ in that order of priority.
148
+
149
+ ```console
150
+ $ envbool --help
151
+ usage: envbool [-h] [--value TEXT] [--strict] [--warn] [--default] [--print]
152
+ [--truthy VALUE] [--falsy VALUE] [--extend-truthy VALUE]
153
+ [--extend-falsy VALUE] [--show-config]
154
+ [VAR_NAME]
155
+
156
+ Coerce an environment variable or string to a boolean.
157
+
158
+ positional arguments:
159
+ VAR_NAME Environment variable name to check.
160
+
161
+ options:
162
+ -h, --help show this help message and exit
163
+ --value, -v TEXT Check a literal string instead of an env var.
164
+ --strict, -s Raise error on unrecognized values.
165
+ --warn Log a warning on unrecognized values.
166
+ --default, -d Default value if unset/empty (default: false).
167
+ --print, -p Print "true" or "false" instead of using exit codes.
168
+ --truthy VALUE Replace the truthy set with VALUE (repeatable).
169
+ --falsy VALUE Replace the falsy set with VALUE (repeatable).
170
+ --extend-truthy VALUE
171
+ Add VALUE to the truthy set (repeatable).
172
+ --extend-falsy VALUE Add VALUE to the falsy set (repeatable).
173
+ --show-config Print the effective configuration and exit.
174
+ ```
175
+
176
+ A few rules worth knowing:
177
+
178
+ - Omitting `--strict` / `--warn` defers to the config file setting.
179
+ - `VAR_NAME` and `--value` are mutually exclusive.
180
+ - `--show-config` prints the effective configuration and exits. It cannot be
181
+ combined with `VAR_NAME`, `--value`, `--print`, or `--default`, but it *can*
182
+ take the value-set flags to preview overrides.
183
+ - With no `VAR_NAME`, `--value`, or piped stdin, the CLI prints usage and exits `2`.
184
+
185
+ ## Configuration
186
+
187
+ Share defaults across a project by dropping an `envbool.toml` at its root (or a
188
+ `[tool.envbool]` table in `pyproject.toml`):
189
+
190
+ ```toml
191
+ # envbool.toml
192
+ strict = true
193
+ extend_truthy = ["enabled"]
194
+ extend_falsy = ["disabled"]
195
+ ```
196
+
197
+ `envbool` walks up from the current directory to find the nearest project config,
198
+ then falls back to a user-level `config.toml` in the platform's standard config
199
+ directory (`~/.config/envbool/` on Linux, `~/Library/Application Support/envbool/`
200
+ on macOS), resolved via [platformdirs](https://pypi.org/project/platformdirs/).
201
+
202
+ Values resolve in three layers, each overriding the last:
203
+
204
+ ```
205
+ built-in defaults → config file → function arguments / CLI flags
206
+ ```
207
+
208
+ Set `ENVBOOL_NO_CONFIG=1` to skip config discovery entirely.
209
+
210
+ ## API reference
211
+
212
+ | Symbol | Description |
213
+ | --- | --- |
214
+ | `envbool(var, **opts)` | Read an environment variable and return `bool`. |
215
+ | `to_bool(value, **opts)` | Coerce a string to `bool`. |
216
+ | `load_config()` | Load and return the active `EnvBoolConfig` (cached). |
217
+ | `EnvBoolConfig` | Frozen dataclass: `strict`, `warn`, `effective_truthy`, `effective_falsy`, `source_path`. |
218
+ | `DEFAULT_TRUTHY` | `frozenset` of the built-in truthy strings. |
219
+ | `DEFAULT_FALSY` | `frozenset` of the built-in falsy strings. |
220
+ | `EnvBoolError` | Base class for every exception the library raises. |
221
+ | `InvalidBoolValueError` | Raised in strict mode for unrecognized values. Also a `ValueError`. |
222
+ | `ConfigError` | Raised when a config file is malformed. |
223
+
224
+ `envbool()` and `to_bool()` share the same keyword-only options:
225
+
226
+ | Option | Type | Default | Meaning |
227
+ | --- | --- | --- | --- |
228
+ | `default` | `bool` | `False` | Returned for unset/empty input. |
229
+ | `strict` | `bool \| None` | `None` | Raise on unrecognized values (`None` defers to config). |
230
+ | `warn` | `bool \| None` | `None` | Log a warning on unrecognized values (`None` defers to config). |
231
+ | `truthy` / `falsy` | `Iterable[str] \| None` | `None` | **Replace** the effective set. |
232
+ | `extend_truthy` / `extend_falsy` | `Iterable[str] \| None` | `None` | **Extend** the effective set. |
233
+
234
+ ## Advanced topics
235
+
236
+ ### Exception handling
237
+
238
+ Every exception inherits from `EnvBoolError`, so a single `except EnvBoolError`
239
+ catches the whole library. Catch a specific subclass when you need its detail:
240
+
241
+ ```python
242
+ from envbool import envbool, InvalidBoolValueError
243
+
244
+ try:
245
+ result = envbool("MY_VAR", strict=True)
246
+ except InvalidBoolValueError as e:
247
+ print(e.var) # "MY_VAR" — env var name, or None when raised from to_bool()
248
+ print(e.value) # "maybe" — the normalized (stripped, lowercased) value
249
+ print(e.truthy) # frozenset({"true", "1", "yes", "on"}) — effective truthy set
250
+ print(e.falsy) # frozenset({"false", "0", "no", "off"}) — effective falsy set
251
+ ```
252
+
253
+ `InvalidBoolValueError` also subclasses the built-in `ValueError`, so existing
254
+ `except ValueError` handlers keep working. Its message spells out exactly what
255
+ was expected:
256
+
257
+ ```
258
+ InvalidBoolValueError: Invalid boolean value for MY_VAR: 'maybe'
259
+ Expected truthy: 1, on, true, yes
260
+ Expected falsy: 0, false, no, off
261
+ ```
262
+
263
+ `ConfigError` is raised when a config file is found but malformed (for example,
264
+ `strict = "yes"` instead of `strict = true`). It carries the offending path on
265
+ `e.path`.
266
+
267
+ ### Logging
268
+
269
+ `envbool` logs through the standard `logging` module under the `"envbool"`
270
+ namespace and attaches no handlers of its own — configure it like any other
271
+ library logger:
272
+
273
+ ```python
274
+ import logging
275
+
276
+ logging.getLogger("envbool").setLevel(logging.DEBUG)
277
+ logging.getLogger("envbool").addHandler(logging.StreamHandler())
278
+ ```
279
+
280
+ | Level | When |
281
+ | --- | --- |
282
+ | `DEBUG` | A config file was discovered and loaded, or none was found. |
283
+ | `WARNING` | An unrecognized value fell through in lenient mode (only when `warn=True`). |
284
+ | `WARNING` | The truthy and falsy sets overlap (truthy wins). |
285
+
286
+ ### The unset-vs-empty distinction
287
+
288
+ `envbool()` always returns `bool` and deliberately cannot tell an unset variable
289
+ apart from one set to the empty string — both yield `default`. Most deployment
290
+ tooling can't distinguish the two either, and a plain `bool` keeps call sites
291
+ clean. When you genuinely need the distinction, check `os.environ` yourself:
292
+
293
+ ```python
294
+ import os
295
+ from envbool import envbool
296
+
297
+ if "MY_VAR" not in os.environ:
298
+ ... # truly unset — handle the "not configured" case
299
+ else:
300
+ result = envbool("MY_VAR")
301
+ ```
302
+
303
+ ### Testing code that uses envbool
304
+
305
+ `envbool` loads its config file once and caches it for the process lifetime. If
306
+ your tests create temporary config files, clear that cache between them with an
307
+ autouse fixture:
308
+
309
+ ```python
310
+ # conftest.py
311
+ import pytest
312
+ from envbool._config import _reset_config
313
+
314
+ @pytest.fixture(autouse=True)
315
+ def _reset_envbool_config():
316
+ yield
317
+ _reset_config()
318
+ ```
319
+
320
+ `_reset_config()` is private but stable and exists for exactly this purpose; it
321
+ clears the cache under a lock, so it is safe to call from any thread.
322
+
323
+ ## Contributing
324
+
325
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for development
326
+ setup, project layout, and the conventions this repo follows.
327
+
328
+ ## License
329
+
330
+ Released under the [MIT License](LICENSE).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "envbool"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  description = "A small Python library and CLI tool for coercing environment variables (and arbitrary strings) into boolean values."
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Kyle O'Malley", email = "j.kyle.omalley@gmail.com" }]