envbool 0.2.0__tar.gz → 0.3.0__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.2.0 → envbool-0.3.0}/PKG-INFO +8 -2
- {envbool-0.2.0 → envbool-0.3.0}/README.md +6 -0
- {envbool-0.2.0 → envbool-0.3.0}/pyproject.toml +3 -2
- {envbool-0.2.0 → envbool-0.3.0}/src/envbool/__init__.py +11 -2
- envbool-0.3.0/src/envbool/__main__.py +6 -0
- {envbool-0.2.0 → envbool-0.3.0}/src/envbool/_cli.py +77 -51
- {envbool-0.2.0 → envbool-0.3.0}/src/envbool/_config.py +23 -1
- {envbool-0.2.0 → envbool-0.3.0}/src/envbool/_env.py +15 -0
- {envbool-0.2.0 → envbool-0.3.0}/src/envbool/exceptions.py +19 -0
- {envbool-0.2.0 → envbool-0.3.0}/src/envbool/_core.py +0 -0
- {envbool-0.2.0 → envbool-0.3.0}/src/envbool/_defaults.py +0 -0
- {envbool-0.2.0 → envbool-0.3.0}/src/envbool/py.typed +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: envbool
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A small Python library and CLI tool for coercing environment variables (and arbitrary strings) into boolean values.
|
|
5
5
|
Keywords: environment variables,boolean,configuration,env,coerce
|
|
6
6
|
Author: Kyle O'Malley
|
|
7
7
|
Author-email: Kyle O'Malley <j.kyle.omalley@gmail.com>
|
|
8
8
|
License-Expression: MIT
|
|
9
|
-
Classifier: Development Status ::
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
12
12
|
Classifier: Topic :: Utilities
|
|
@@ -242,11 +242,13 @@ Set `ENVBOOL_NO_CONFIG=1` to skip config discovery entirely.
|
|
|
242
242
|
| `envbool(var, **opts)` | Read an environment variable and return `bool`. |
|
|
243
243
|
| `to_bool(value, **opts)` | Coerce a string to `bool`. |
|
|
244
244
|
| `load_config()` | Load and return the active `EnvBoolConfig` (cached). |
|
|
245
|
+
| `reload_config()` | Discard the cache, re-read the config file, and return the fresh `EnvBoolConfig`. |
|
|
245
246
|
| `EnvBoolConfig` | Frozen dataclass: `strict`, `warn`, `effective_truthy`, `effective_falsy`, `source_path`. |
|
|
246
247
|
| `DEFAULT_TRUTHY` | `frozenset` of the built-in truthy strings. |
|
|
247
248
|
| `DEFAULT_FALSY` | `frozenset` of the built-in falsy strings. |
|
|
248
249
|
| `EnvBoolError` | Base class for every exception the library raises. |
|
|
249
250
|
| `InvalidBoolValueError` | Raised in strict mode for unrecognized values. Also a `ValueError`. |
|
|
251
|
+
| `MissingEnvVarError` | Raised by `envbool(required=True)` when the variable is unset. Also a `KeyError`. |
|
|
250
252
|
| `ConfigError` | Raised when a config file is malformed. |
|
|
251
253
|
|
|
252
254
|
`envbool()` and `to_bool()` share the same keyword-only options:
|
|
@@ -259,6 +261,10 @@ Set `ENVBOOL_NO_CONFIG=1` to skip config discovery entirely.
|
|
|
259
261
|
| `truthy` / `falsy` | `Iterable[str] \| None` | `None` | **Replace** the effective set. |
|
|
260
262
|
| `extend_truthy` / `extend_falsy` | `Iterable[str] \| None` | `None` | **Extend** the effective set. |
|
|
261
263
|
|
|
264
|
+
`envbool()` also accepts `required` (`bool`, default `False`): when `True`, a
|
|
265
|
+
variable that is unset raises `MissingEnvVarError` before `default` is applied. A
|
|
266
|
+
variable set to an empty string counts as present and still uses `default`.
|
|
267
|
+
|
|
262
268
|
## Advanced topics
|
|
263
269
|
|
|
264
270
|
### Exception handling
|
|
@@ -214,11 +214,13 @@ Set `ENVBOOL_NO_CONFIG=1` to skip config discovery entirely.
|
|
|
214
214
|
| `envbool(var, **opts)` | Read an environment variable and return `bool`. |
|
|
215
215
|
| `to_bool(value, **opts)` | Coerce a string to `bool`. |
|
|
216
216
|
| `load_config()` | Load and return the active `EnvBoolConfig` (cached). |
|
|
217
|
+
| `reload_config()` | Discard the cache, re-read the config file, and return the fresh `EnvBoolConfig`. |
|
|
217
218
|
| `EnvBoolConfig` | Frozen dataclass: `strict`, `warn`, `effective_truthy`, `effective_falsy`, `source_path`. |
|
|
218
219
|
| `DEFAULT_TRUTHY` | `frozenset` of the built-in truthy strings. |
|
|
219
220
|
| `DEFAULT_FALSY` | `frozenset` of the built-in falsy strings. |
|
|
220
221
|
| `EnvBoolError` | Base class for every exception the library raises. |
|
|
221
222
|
| `InvalidBoolValueError` | Raised in strict mode for unrecognized values. Also a `ValueError`. |
|
|
223
|
+
| `MissingEnvVarError` | Raised by `envbool(required=True)` when the variable is unset. Also a `KeyError`. |
|
|
222
224
|
| `ConfigError` | Raised when a config file is malformed. |
|
|
223
225
|
|
|
224
226
|
`envbool()` and `to_bool()` share the same keyword-only options:
|
|
@@ -231,6 +233,10 @@ Set `ENVBOOL_NO_CONFIG=1` to skip config discovery entirely.
|
|
|
231
233
|
| `truthy` / `falsy` | `Iterable[str] \| None` | `None` | **Replace** the effective set. |
|
|
232
234
|
| `extend_truthy` / `extend_falsy` | `Iterable[str] \| None` | `None` | **Extend** the effective set. |
|
|
233
235
|
|
|
236
|
+
`envbool()` also accepts `required` (`bool`, default `False`): when `True`, a
|
|
237
|
+
variable that is unset raises `MissingEnvVarError` before `default` is applied. A
|
|
238
|
+
variable set to an empty string counts as present and still uses `default`.
|
|
239
|
+
|
|
234
240
|
## Advanced topics
|
|
235
241
|
|
|
236
242
|
### Exception handling
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "envbool"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
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" }]
|
|
@@ -11,7 +11,7 @@ dependencies = [
|
|
|
11
11
|
"platformdirs>=4.9.6",
|
|
12
12
|
]
|
|
13
13
|
classifiers = [
|
|
14
|
-
"Development Status ::
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
15
|
"Intended Audience :: Developers",
|
|
16
16
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
17
17
|
"Topic :: Utilities",
|
|
@@ -40,6 +40,7 @@ build-backend = "uv_build"
|
|
|
40
40
|
|
|
41
41
|
[dependency-groups]
|
|
42
42
|
dev = [
|
|
43
|
+
"hypothesis>=6.155.5",
|
|
43
44
|
"pre-commit>=4.5.1",
|
|
44
45
|
"pytest>=9.0.3",
|
|
45
46
|
"pytest-cov>=7.1.0",
|
|
@@ -12,22 +12,29 @@ Available names:
|
|
|
12
12
|
envbool() -- read an env var and coerce to bool (primary API)
|
|
13
13
|
to_bool() -- coerce an arbitrary string to bool (no os.environ)
|
|
14
14
|
load_config() -- inspect or preload the process-level config cache
|
|
15
|
+
reload_config() -- discard the cache and re-read the config file
|
|
15
16
|
EnvBoolConfig -- frozen dataclass returned by load_config()
|
|
16
17
|
DEFAULT_TRUTHY -- built-in truthy set (frozenset)
|
|
17
18
|
DEFAULT_FALSY -- built-in falsy set (frozenset)
|
|
18
19
|
EnvBoolError -- base exception for all envbool errors
|
|
19
20
|
InvalidBoolValueError -- raised in strict mode for unrecognized values
|
|
21
|
+
MissingEnvVarError -- raised by envbool(required=True) when a var is unset
|
|
20
22
|
ConfigError -- raised for malformed or unreadable config files
|
|
21
23
|
"""
|
|
22
24
|
# All implementation lives in private underscore-prefixed modules so the public
|
|
23
25
|
# surface can be reshaped without breaking imports. Do not import from _core,
|
|
24
26
|
# _env, _config, _cli, or _defaults directly.
|
|
25
27
|
|
|
26
|
-
from envbool._config import EnvBoolConfig, load_config
|
|
28
|
+
from envbool._config import EnvBoolConfig, load_config, reload_config
|
|
27
29
|
from envbool._core import to_bool
|
|
28
30
|
from envbool._defaults import DEFAULT_FALSY, DEFAULT_TRUTHY
|
|
29
31
|
from envbool._env import envbool
|
|
30
|
-
from envbool.exceptions import
|
|
32
|
+
from envbool.exceptions import (
|
|
33
|
+
ConfigError,
|
|
34
|
+
EnvBoolError,
|
|
35
|
+
InvalidBoolValueError,
|
|
36
|
+
MissingEnvVarError,
|
|
37
|
+
)
|
|
31
38
|
|
|
32
39
|
__all__ = [
|
|
33
40
|
"DEFAULT_FALSY",
|
|
@@ -36,7 +43,9 @@ __all__ = [
|
|
|
36
43
|
"EnvBoolConfig",
|
|
37
44
|
"EnvBoolError",
|
|
38
45
|
"InvalidBoolValueError",
|
|
46
|
+
"MissingEnvVarError",
|
|
39
47
|
"envbool",
|
|
40
48
|
"load_config",
|
|
49
|
+
"reload_config",
|
|
41
50
|
"to_bool",
|
|
42
51
|
]
|
|
@@ -11,7 +11,8 @@ Input source (first match wins):
|
|
|
11
11
|
Exit codes:
|
|
12
12
|
0 -- truthy
|
|
13
13
|
1 -- falsy or unset/empty
|
|
14
|
-
2 -- error (unrecognized value in strict mode,
|
|
14
|
+
2 -- error (unrecognized value in strict mode, unset VAR_NAME with --required,
|
|
15
|
+
bad arguments, multi-line stdin)
|
|
15
16
|
|
|
16
17
|
Omitting --strict or --warn defers to the config file setting (default:
|
|
17
18
|
lenient, no warnings).
|
|
@@ -40,7 +41,7 @@ import sys
|
|
|
40
41
|
from envbool._config import load_config
|
|
41
42
|
from envbool._core import _resolve, to_bool
|
|
42
43
|
from envbool._env import envbool
|
|
43
|
-
from envbool.exceptions import ConfigError, InvalidBoolValueError
|
|
44
|
+
from envbool.exceptions import ConfigError, InvalidBoolValueError, MissingEnvVarError
|
|
44
45
|
|
|
45
46
|
|
|
46
47
|
def _build_parser() -> argparse.ArgumentParser:
|
|
@@ -86,6 +87,13 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
86
87
|
default=False,
|
|
87
88
|
help="Default value if unset/empty (default: false).",
|
|
88
89
|
)
|
|
90
|
+
parser.add_argument(
|
|
91
|
+
"--required",
|
|
92
|
+
"-r",
|
|
93
|
+
action="store_true",
|
|
94
|
+
default=False,
|
|
95
|
+
help="Exit 2 if VAR_NAME is not set in the environment.",
|
|
96
|
+
)
|
|
89
97
|
parser.add_argument(
|
|
90
98
|
"--print",
|
|
91
99
|
"-p",
|
|
@@ -146,12 +154,15 @@ def _print_config(args: argparse.Namespace) -> None:
|
|
|
146
154
|
print(f"falsy: {', '.join(sorted(effective_falsy))}")
|
|
147
155
|
|
|
148
156
|
|
|
149
|
-
def
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
args = parser.parse_args()
|
|
157
|
+
def _coerce_from_source(
|
|
158
|
+
parser: argparse.ArgumentParser, args: argparse.Namespace
|
|
159
|
+
) -> bool:
|
|
160
|
+
"""Resolve the input source (--value, VAR_NAME, or stdin) and coerce it to bool.
|
|
154
161
|
|
|
162
|
+
Validates the mutually exclusive source flags, then dispatches to the matching
|
|
163
|
+
coercion call. Exits via parser.error()/sys.exit() for usage errors; the
|
|
164
|
+
coercion exceptions propagate to main()'s single error boundary.
|
|
165
|
+
"""
|
|
155
166
|
value_set_kwargs = {
|
|
156
167
|
"truthy": args.truthy,
|
|
157
168
|
"falsy": args.falsy,
|
|
@@ -159,6 +170,63 @@ def main() -> None:
|
|
|
159
170
|
"extend_falsy": args.extend_falsy,
|
|
160
171
|
}
|
|
161
172
|
|
|
173
|
+
# --value and VAR_NAME are mutually exclusive. Using argparse's built-in
|
|
174
|
+
# add_mutually_exclusive_group would place them in a separate usage section,
|
|
175
|
+
# which makes the help text harder to read, so we validate manually instead.
|
|
176
|
+
if args.value is not None and args.var is not None:
|
|
177
|
+
parser.error("VAR_NAME and --value are mutually exclusive")
|
|
178
|
+
|
|
179
|
+
# --required only governs the env-var lookup; a literal --value always
|
|
180
|
+
# has a value, so combining them is a usage error rather than a no-op.
|
|
181
|
+
if args.required and args.value is not None:
|
|
182
|
+
parser.error("--required and --value are mutually exclusive")
|
|
183
|
+
|
|
184
|
+
if args.value is not None:
|
|
185
|
+
return to_bool(
|
|
186
|
+
args.value,
|
|
187
|
+
strict=args.strict,
|
|
188
|
+
warn=args.warn,
|
|
189
|
+
default=args.default,
|
|
190
|
+
**value_set_kwargs,
|
|
191
|
+
)
|
|
192
|
+
if args.var is not None:
|
|
193
|
+
return envbool(
|
|
194
|
+
args.var,
|
|
195
|
+
strict=args.strict,
|
|
196
|
+
warn=args.warn,
|
|
197
|
+
default=args.default,
|
|
198
|
+
required=args.required,
|
|
199
|
+
**value_set_kwargs,
|
|
200
|
+
)
|
|
201
|
+
if not sys.stdin.isatty():
|
|
202
|
+
# Non-TTY stdin means the user piped or redirected input. Strip surrounding
|
|
203
|
+
# whitespace (handles the trailing newline echo adds) then reject anything
|
|
204
|
+
# with an embedded newline -- only a single value is meaningful here.
|
|
205
|
+
raw = sys.stdin.read().strip()
|
|
206
|
+
if "\n" in raw:
|
|
207
|
+
print(
|
|
208
|
+
"error: stdin must contain a single value, not multiple lines",
|
|
209
|
+
file=sys.stderr,
|
|
210
|
+
)
|
|
211
|
+
sys.exit(2)
|
|
212
|
+
return to_bool(
|
|
213
|
+
raw,
|
|
214
|
+
strict=args.strict,
|
|
215
|
+
warn=args.warn,
|
|
216
|
+
default=args.default,
|
|
217
|
+
**value_set_kwargs,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
parser.print_usage(sys.stderr)
|
|
221
|
+
sys.exit(2)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def main() -> None:
|
|
225
|
+
"""Parse arguments, resolve the input source, and exit with the appropriate code."""
|
|
226
|
+
# All coercion logic lives in _core.py; this function is pure I/O plumbing.
|
|
227
|
+
parser = _build_parser()
|
|
228
|
+
args = parser.parse_args()
|
|
229
|
+
|
|
162
230
|
# Both --show-config and the coercion calls below trigger config-file loading,
|
|
163
231
|
# so a malformed config can raise ConfigError from either path. A single error
|
|
164
232
|
# boundary around both keeps that failure a clean "error: ..." exit rather than
|
|
@@ -180,50 +248,8 @@ def main() -> None:
|
|
|
180
248
|
_print_config(args)
|
|
181
249
|
sys.exit(0)
|
|
182
250
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
# which makes the help text harder to read, so we validate manually instead.
|
|
186
|
-
if args.value is not None and args.var is not None:
|
|
187
|
-
parser.error("VAR_NAME and --value are mutually exclusive")
|
|
188
|
-
|
|
189
|
-
if args.value is not None:
|
|
190
|
-
result = to_bool(
|
|
191
|
-
args.value,
|
|
192
|
-
strict=args.strict,
|
|
193
|
-
warn=args.warn,
|
|
194
|
-
default=args.default,
|
|
195
|
-
**value_set_kwargs,
|
|
196
|
-
)
|
|
197
|
-
elif args.var is not None:
|
|
198
|
-
result = envbool(
|
|
199
|
-
args.var,
|
|
200
|
-
strict=args.strict,
|
|
201
|
-
warn=args.warn,
|
|
202
|
-
default=args.default,
|
|
203
|
-
**value_set_kwargs,
|
|
204
|
-
)
|
|
205
|
-
elif not sys.stdin.isatty():
|
|
206
|
-
# Non-TTY stdin means the user piped or redirected input. Strip surrounding
|
|
207
|
-
# whitespace (handles the trailing newline echo adds) then reject anything
|
|
208
|
-
# with an embedded newline -- only a single value is meaningful here.
|
|
209
|
-
raw = sys.stdin.read().strip()
|
|
210
|
-
if "\n" in raw:
|
|
211
|
-
print(
|
|
212
|
-
"error: stdin must contain a single value, not multiple lines",
|
|
213
|
-
file=sys.stderr,
|
|
214
|
-
)
|
|
215
|
-
sys.exit(2)
|
|
216
|
-
result = to_bool(
|
|
217
|
-
raw,
|
|
218
|
-
strict=args.strict,
|
|
219
|
-
warn=args.warn,
|
|
220
|
-
default=args.default,
|
|
221
|
-
**value_set_kwargs,
|
|
222
|
-
)
|
|
223
|
-
else:
|
|
224
|
-
parser.print_usage(sys.stderr)
|
|
225
|
-
sys.exit(2)
|
|
226
|
-
except (InvalidBoolValueError, ConfigError) as e:
|
|
251
|
+
result = _coerce_from_source(parser, args)
|
|
252
|
+
except (InvalidBoolValueError, ConfigError, MissingEnvVarError) as e:
|
|
227
253
|
print(f"error: {e}", file=sys.stderr)
|
|
228
254
|
sys.exit(2)
|
|
229
255
|
|
|
@@ -26,7 +26,7 @@ import platformdirs
|
|
|
26
26
|
from envbool._defaults import DEFAULT_FALSY, DEFAULT_TRUTHY
|
|
27
27
|
from envbool.exceptions import ConfigError
|
|
28
28
|
|
|
29
|
-
__all__ = ["EnvBoolConfig", "load_config"]
|
|
29
|
+
__all__ = ["EnvBoolConfig", "load_config", "reload_config"]
|
|
30
30
|
|
|
31
31
|
_logger = logging.getLogger(__name__)
|
|
32
32
|
|
|
@@ -89,6 +89,28 @@ def load_config() -> EnvBoolConfig:
|
|
|
89
89
|
return _get_config()
|
|
90
90
|
|
|
91
91
|
|
|
92
|
+
def reload_config() -> EnvBoolConfig:
|
|
93
|
+
"""Force a re-read of the config file, replacing the cached config.
|
|
94
|
+
|
|
95
|
+
``load_config()`` caches for the lifetime of the process, so a config file
|
|
96
|
+
that is written or edited after the first lookup is never picked up. Call
|
|
97
|
+
this to discard the cache and reload from disk -- useful in long-running
|
|
98
|
+
processes (workers, servers, notebooks) that change config at runtime.
|
|
99
|
+
|
|
100
|
+
The clear-and-reload happens atomically under the cache lock so concurrent
|
|
101
|
+
readers never observe a momentarily empty cache.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
The freshly loaded EnvBoolConfig, now installed as the cached instance.
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
ConfigError: If a config file is found but malformed or has invalid values.
|
|
108
|
+
"""
|
|
109
|
+
with _cache.lock:
|
|
110
|
+
_cache.config = _load_config_from_disk()
|
|
111
|
+
return _cache.config
|
|
112
|
+
|
|
113
|
+
|
|
92
114
|
def _get_config() -> EnvBoolConfig:
|
|
93
115
|
# Double-checked locking: the outer check avoids lock contention on the hot
|
|
94
116
|
# path (all calls after first load); the inner check prevents duplicate disk
|
|
@@ -14,12 +14,14 @@ import os
|
|
|
14
14
|
from collections.abc import Iterable
|
|
15
15
|
|
|
16
16
|
from envbool._core import to_bool
|
|
17
|
+
from envbool.exceptions import MissingEnvVarError
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def envbool(
|
|
20
21
|
var: str,
|
|
21
22
|
*,
|
|
22
23
|
default: bool = False,
|
|
24
|
+
required: bool = False,
|
|
23
25
|
strict: bool | None = None,
|
|
24
26
|
warn: bool | None = None,
|
|
25
27
|
truthy: Iterable[str] | None = None,
|
|
@@ -32,6 +34,9 @@ def envbool(
|
|
|
32
34
|
Args:
|
|
33
35
|
var: Environment variable name.
|
|
34
36
|
default: Returned when the variable is unset or empty.
|
|
37
|
+
required: When True, raise if the variable is not set at all. A truly
|
|
38
|
+
unset var raises before `default` is considered; a var set to an
|
|
39
|
+
empty string is "present" and still coerces via `default`.
|
|
35
40
|
strict: Raise on unrecognized values. None defers to config (default False).
|
|
36
41
|
warn: Log a warning on unrecognized values. None defers to config
|
|
37
42
|
(default False).
|
|
@@ -45,7 +50,17 @@ def envbool(
|
|
|
45
50
|
|
|
46
51
|
Raises:
|
|
47
52
|
InvalidBoolValueError: In strict mode when the value is unrecognized.
|
|
53
|
+
MissingEnvVarError: When required=True and the variable is unset.
|
|
48
54
|
"""
|
|
55
|
+
# `required` distinguishes "absent from the environment" from "set but empty"
|
|
56
|
+
# -- membership, not the value, so VAR= (empty) is treated as present. This
|
|
57
|
+
# check precedes the default-handling below so an unset required var raises
|
|
58
|
+
# instead of silently falling back to `default`.
|
|
59
|
+
if required and var not in os.environ:
|
|
60
|
+
err = MissingEnvVarError(f"Required environment variable {var} is not set")
|
|
61
|
+
err.var = var
|
|
62
|
+
raise err
|
|
63
|
+
|
|
49
64
|
# Missing var becomes "" so to_bool treats it the same as an empty value,
|
|
50
65
|
# returning `default` rather than raising or treating absence as a distinct state.
|
|
51
66
|
value = os.environ.get(var, "")
|
|
@@ -8,6 +8,7 @@ which predates envbool adoption keeps working without changes.
|
|
|
8
8
|
Hierarchy:
|
|
9
9
|
EnvBoolError(Exception)
|
|
10
10
|
InvalidBoolValueError(EnvBoolError, ValueError)
|
|
11
|
+
MissingEnvVarError(EnvBoolError, KeyError)
|
|
11
12
|
ConfigError(EnvBoolError)
|
|
12
13
|
"""
|
|
13
14
|
|
|
@@ -44,6 +45,24 @@ class InvalidBoolValueError(EnvBoolError, ValueError):
|
|
|
44
45
|
falsy: frozenset[str]
|
|
45
46
|
|
|
46
47
|
|
|
48
|
+
class MissingEnvVarError(EnvBoolError, KeyError):
|
|
49
|
+
"""Raised by envbool(var, required=True) when var is not set in the environment.
|
|
50
|
+
|
|
51
|
+
Dual inheritance with KeyError mirrors the InvalidBoolValueError(ValueError)
|
|
52
|
+
pattern: a missing os.environ lookup naturally raises KeyError, so existing
|
|
53
|
+
``except KeyError`` handlers keep working after a codebase adopts envbool.
|
|
54
|
+
|
|
55
|
+
Only a truly-unset variable triggers this -- a variable set to an empty
|
|
56
|
+
string is "present" and coerces normally via the caller's default.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
# Set by the raising code after construction (see InvalidBoolValueError for
|
|
60
|
+
# why attributes live here rather than in __init__).
|
|
61
|
+
|
|
62
|
+
# Name of the environment variable that was required but not set.
|
|
63
|
+
var: str
|
|
64
|
+
|
|
65
|
+
|
|
47
66
|
class ConfigError(EnvBoolError):
|
|
48
67
|
"""Raised when a config file is malformed or contains invalid values.
|
|
49
68
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|