Skip to main content

envbool

Coerce environment variables and strings into booleans — sensibly.

PyPI version Python versions License: MIT CI


Reading a boolean out of the environment is the kind of thing every project reinvents, slightly differently, in slightly buggy ways:

DEBUG   = os.environ.get("DEBUG",   "").lower() in ("1", "true", "yes")
VERBOSE = os.environ.get("VERBOSE", "").lower() in ("1", "true", "yes")
CACHE   = os.environ.get("CACHE",   "").lower() in ("1", "true", "yes")

envbool is that snippet, done once and done properly:

from envbool import envbool

DEBUG   = envbool("DEBUG")
VERBOSE = envbool("VERBOSE")
CACHE   = envbool("CACHE")

Features

  • Lenient by default, strict when you want it. Unrecognized values quietly become False, or raise on demand to catch typos in production config.
  • Always returns bool. No None, no surprises in your type signatures.
  • Customizable value sets. Replace or extend the truthy/falsy words your environment uses.
  • Process-level defaults. Call set_defaults() once at startup instead of threading options through every call site.
  • A CLI for shell scripts. Exit codes map to truthiness, so it drops straight into && / || chains.
  • Zero ceremony. Zero dependencies, fully typed, Python 3.11+.

Contents

Installation

pip install envbool
# or
uv add envbool

Usage

The basics

envbool is lenient by default: anything not recognized as truthy returns False, and unset or empty variables return the default.

from envbool import envbool

DEBUG = envbool("DEBUG")                 # False if unset or empty
CACHE = envbool("CACHE", default=True)   # True if unset or empty

The built-in truthy values are true, 1, yes, on; the falsy values are false, 0, no, off. Comparison is case-insensitive and ignores surrounding whitespace.

Strict mode

Pass strict=True to raise InvalidBoolValueError on anything outside the truthy/falsy sets — ideal for failing fast on a misconfigured deployment.

import sys
from envbool import envbool, InvalidBoolValueError

try:
    USE_SSL = envbool("USE_SSL", strict=True)
except InvalidBoolValueError as e:
    sys.exit(f"Bad value for USE_SSL: {e.value!r}")

Custom value sets

When your environment speaks a different dialect, extend the defaults or replace them outright:

# Add to the built-in sets
FEATURE = envbool("FEATURE_FLAG", extend_truthy={"enabled", "y"})

# Replace them entirely
LOCALE = envbool("USE_METRIC", truthy={"metric"}, falsy={"imperial"})

Coercing arbitrary strings

Use to_bool for values that don't come from the environment. It accepts the same keyword arguments as envbool.

from envbool import to_bool

to_bool("yes")                 # True
to_bool("0")                   # False
to_bool("maybe", strict=True)  # raises InvalidBoolValueError

Process-level defaults

Set policy once at startup instead of threading strict=/extend_truthy= through every call site:

import envbool

envbool.set_defaults(strict=True, extend_truthy=["enabled"])

envbool.envbool("DEBUG")  # now raises on unrecognized values by default

set_defaults() replaces the process-level defaults from the built-ins, not from whatever a previous set_defaults() call left in place — call it once. Call-site arguments (envbool("X", strict=False)) still override whatever set_defaults() configured:

built-in defaults  →  set_defaults()  →  function arguments / CLI flags

get_defaults() returns the active Defaults (a frozen dataclass: strict, warn, effective_truthy, effective_falsy) for inspection. reset_defaults() restores the built-ins — call it in a test fixture (see Testing code that uses envbool).

Through 0.3.x, envbool read TOML config files (envbool.toml, [tool.envbool]). 0.4.0 removed them in favor of set_defaults() — see CHANGELOG.md for the rationale and migration note.

Command-line interface

The envbool command exits 0 for truthy, 1 for falsy, and 2 on error, so it composes naturally with shell control flow.

$ export DEBUG=true
$ envbool DEBUG && echo "debug is on"
debug is on

$ echo "Verbose: $(envbool --print VERBOSE)"
Verbose: false

$ echo "yes" | envbool && echo "truthy"
truthy

$ envbool --strict ENABLE_CACHE || echo "cache is off or misconfigured"
cache is off or misconfigured

Input is taken from a VAR_NAME argument, the --value flag, or a stdin pipe — in that order of priority.

$ envbool --help
usage: envbool [-h] [--value TEXT] [--strict] [--warn] [--default]
               [--required] [--print] [--truthy VALUE] [--falsy VALUE]
               [--extend-truthy VALUE] [--extend-falsy VALUE]
               [VAR_NAME]

Coerce an environment variable or string to a boolean.

positional arguments:
  VAR_NAME              Environment variable name to check.

options:
  -h, --help            show this help message and exit
  --value, -v TEXT      Check a literal string instead of an env var.
  --strict, -s          Raise error on unrecognized values.
  --warn                Log a warning on unrecognized values.
  --default, -d         Default value if unset/empty (default: false).
  --required, -r        Exit 2 if VAR_NAME is not set in the environment.
  --print, -p           Print "true" or "false" instead of using exit codes.
  --truthy VALUE        Replace the truthy set with VALUE (repeatable).
  --falsy VALUE         Replace the falsy set with VALUE (repeatable).
  --extend-truthy VALUE
                        Add VALUE to the truthy set (repeatable).
  --extend-falsy VALUE  Add VALUE to the falsy set (repeatable).

A few rules worth knowing:

  • Omitting --strict / --warn uses the built-in defaults (lenient, no warnings). set_defaults() is a library-level concern — the one-shot CLI process doesn't read it.
  • VAR_NAME and --value are mutually exclusive.
  • --required only applies to VAR_NAME; combining it with --value or giving it no VAR_NAME at all is a usage error.
  • With no VAR_NAME, --value, or non-empty piped stdin, the CLI prints usage and exits 2.

API reference

Symbol Description
envbool(var, **opts) Read an environment variable and return bool.
to_bool(value, **opts) Coerce a string to bool.
set_defaults(**opts) Set process-level strict/warn/truthy/falsy defaults, replacing the built-ins.
get_defaults() Return the active Defaults.
reset_defaults() Restore built-in defaults.
Defaults Frozen dataclass: strict, warn, effective_truthy, effective_falsy.
DEFAULT_TRUTHY frozenset of the built-in truthy strings.
DEFAULT_FALSY frozenset of the built-in falsy strings.
EnvBoolError Base class for every exception the library raises.
InvalidBoolValueError Raised in strict mode for unrecognized values. Also a ValueError.
MissingEnvVarError Raised by envbool(required=True) when the variable is unset. Also a KeyError.

envbool() and to_bool() share the same keyword-only options:

Option Type Default Meaning
default bool False Returned for unset/empty input.
strict bool | None None Raise on unrecognized values (None defers to set_defaults()).
warn bool | None None Log a warning on unrecognized values (None defers to set_defaults()).
truthy / falsy Iterable[str] | None None Replace the effective set.
extend_truthy / extend_falsy Iterable[str] | None None Extend the effective set.

envbool() also accepts required (bool, default False): when True, a variable that is unset raises MissingEnvVarError before default is applied. A variable set to an empty string counts as present and still uses default.

Advanced topics

Exception handling

Every exception inherits from EnvBoolError, so a single except EnvBoolError catches the whole library. Catch a specific subclass when you need its detail:

from envbool import envbool, InvalidBoolValueError

try:
    result = envbool("MY_VAR", strict=True)
except InvalidBoolValueError as e:
    print(e.var)    # "MY_VAR" — env var name, or None when raised from to_bool()
    print(e.value)  # "maybe" — the normalized (stripped, lowercased) value
    print(e.truthy) # frozenset({"true", "1", "yes", "on"}) — effective truthy set
    print(e.falsy)  # frozenset({"false", "0", "no", "off"}) — effective falsy set

InvalidBoolValueError also subclasses the built-in ValueError, so existing except ValueError handlers keep working. Its message spells out exactly what was expected:

InvalidBoolValueError: Invalid boolean value for MY_VAR: 'maybe'
  Expected truthy: 1, on, true, yes
  Expected falsy:  0, false, no, off

Logging

envbool logs through the standard logging module under the "envbool" namespace and attaches no handlers of its own — configure it like any other library logger:

import logging

logging.getLogger("envbool").setLevel(logging.DEBUG)
logging.getLogger("envbool").addHandler(logging.StreamHandler())
Level When
WARNING An unrecognized value fell through in lenient mode (only when warn=True).
WARNING The truthy and falsy sets overlap (truthy wins).

The unset-vs-empty distinction

envbool() always returns bool and deliberately cannot tell an unset variable apart from one set to the empty string — both yield default. Most deployment tooling can't distinguish the two either, and a plain bool keeps call sites clean. When you genuinely need the distinction, check os.environ yourself:

import os
from envbool import envbool

if "MY_VAR" not in os.environ:
    ...  # truly unset — handle the "not configured" case
else:
    result = envbool("MY_VAR")

Testing code that uses envbool

If your tests call set_defaults(), reset it between tests with an autouse fixture so overrides don't leak across the suite:

# conftest.py
import pytest
from envbool import reset_defaults

@pytest.fixture(autouse=True)
def _reset_envbool_defaults():
    yield
    reset_defaults()

Contributing

Contributions are welcome. See CONTRIBUTING.md for development setup, project layout, and the conventions this repo follows.

License

Released under the MIT License.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

envbool-0.4.0.tar.gz (14.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

envbool-0.4.0-py3-none-any.whl (17.1 kB view details)

Uploaded Python 3

File details

Details for the file envbool-0.4.0.tar.gz.

File metadata

  • Download URL: envbool-0.4.0.tar.gz
  • Upload date:
  • Size: 14.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for envbool-0.4.0.tar.gz
Algorithm Hash digest
SHA256 a5188c44924f6c7898319b80277d8f4afb3fcd546e527df2ab7cdd8276686870
MD5 21a738c35f5e2eb418110017483b9a1f
BLAKE2b-256 d0d0400792e7c4261f1904d8de561a206b56d5f8a659bc273f719cc0df4249f3

See more details on using hashes here.

Provenance

The following attestation bundles were made for envbool-0.4.0.tar.gz:

Publisher: cd.yml on jkomalley/envbool

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file envbool-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: envbool-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 17.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for envbool-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fb33d602eff226cbc19a55300a06c23f521eeda5c1b5ec282c8b4a6d4886c11a
MD5 ab4413698af226cd93b7695c2431c846
BLAKE2b-256 23e4fd335dc54af558271711c5d0e68388097f790c357f6371102436675b9a8b

See more details on using hashes here.

Provenance

The following attestation bundles were made for envbool-0.4.0-py3-none-any.whl:

Publisher: cd.yml on jkomalley/envbool

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page