Why must you call configparser's getboolean() instead of bool() on a config value?
answer
- INI files carry no types at all
- which strings are falsy in Python
- one vocabulary, not truthiness
- yes/no/on/off/true/false/1/0
- an unknown word must raise ValueError
basics
~20 sEvery value configparser returns is a str, and bool() of any non-empty string is True — so bool('false') is True. getboolean() maps a fixed vocabulary (1/yes/true/on and 0/no/false/off, case-insensitively) to a real bool and raises ValueError on anything else.
solid answer
~40 sINI files have no types: `cfg["extract"]["fast"]` is the string `"false"`, and `bool` on a non-empty string is `True`, so the naive conversion silently inverts the operator's intent — one of the most common configuration bugs in Python. `configparser` therefore ships typed accessors: `getboolean` recognises `"1"`, `"yes"`, `"true"`, `"on"` as true and `"0"`, `"no"`, `"false"`, `"off"` as false, case-insensitively, and raises `ValueError` for anything else; `getint` and `getfloat` just apply `int()` and `float()` and raise `ValueError` on junk. All three exist both on the parser (`cfg.getboolean(section, option)`) and on a section proxy (`cfg["extract"].getboolean("fast")`), and all take `fallback=` for a missing option. The accepted true/false words live in the parser's `BOOLEAN_STATES` mapping, and the `converters=` argument adds your own typed getters.
code
python · 15 linesimport configparser
cfg = configparser.ConfigParser()
cfg.read_string("[extract]\nfast = no\nworkers = 4\n")
print(bool(cfg["extract"]["fast"])) # True - non-empty string
print(cfg["extract"].getboolean("fast")) # False
print(cfg.getint("extract", "workers") + 1) # 5
print(cfg.getboolean("extract", "probe", fallback=False))
cfg.read_string("[extract]\nfast = maybe\n")
try:
cfg["extract"].getboolean("fast")
except ValueError as exc:
print("ValueError:", exc)go deeper
Remember that configparser hands back strings and that every non-empty string is truthy, so bool('false') is True. Reach for getboolean, getint and getfloat instead of built-in constructors.
Explain the mechanism: getboolean normalises the text and looks it up in a fixed true/false vocabulary, raising ValueError otherwise, while getint and getfloat delegate to the constructors. Know the parser and section-proxy call shapes and fallback=.
Show the production judgement: a bad config value should stop startup, not fall back silently; the same trap governs environment overrides; and converters= centralises list or duration parsing rather than scattering split calls.
Own the policy. Decide where typed configuration formats remove this class of bug entirely, what your services do on an unparsable value, and how a boolean vocabulary stays consistent across files, environment variables and command-line flags.
## The bug this method exists to prevent An INI file is untyped text. When you read `fast = false` with `configparser`, you get back the four-character string `"false"`. Passing it to `bool()` asks a completely different question — *is this string non-empty?* — and every non-empty string is truthy. So: ```python bool("false") # True bool("0") # True bool("no") # True bool("") # False (the only false string) ``` The operator wrote `false`, the program read `True`, and nothing raised. That silence is what makes it dangerous: the config knob appears to be wired up, the tests that only ever set it to `true` pass, and the wrong branch runs in production. It is the same trap as `bool(os.environ.get("DEBUG", ""))`, because environment variable values are also always strings. ## What getboolean actually does `getboolean` does not use truthiness. It normalises the string (strip and lower-case it) and looks it up in a small mapping of accepted words, exposed as `configparser.RawConfigParser.BOOLEAN_STATES`: * true: `"1"`, `"yes"`, `"true"`, `"on"` * false: `"0"`, `"no"`, `"false"`, `"off"` Anything else — `"none"`, `"disabled"`, `"y"`, `"True "` with a stray character, a typo — raises `ValueError`. That is the second half of the value: an unrecognised word is a *loud* failure at read time rather than a silently-true flag. Because `BOOLEAN_STATES` is an ordinary class attribute, a parser subclass can extend the vocabulary if a legacy file uses `enabled`/`disabled`, though widening it is usually worse than fixing the file. `getint` and `getfloat` are thinner: they call `int()` and `float()` on the string and let those raise `ValueError` on bad input. `getint` does not accept `"4.0"` and does not silently truncate; `getfloat("1_000.5")` follows the ordinary numeric-literal rules of the constructors. ## The two call shapes, and fallback All the typed accessors exist twice: ```python cfg.getboolean("extract", "fast") # parser-level: section and option cfg["extract"].getboolean("fast") # section proxy: option only ``` Both accept `fallback=`, which is what you want for an option that may be absent: `cfg.getboolean("extract", "fast", fallback=False)` returns the fallback instead of raising `NoOptionError`. They also accept `raw=` and `vars=`, the same interpolation controls as `get`. Note that `fallback` covers a *missing* option, not an *invalid* one — a present-but-unparsable value still raises `ValueError`, and that is correct: a typo in config should stop the process, not be quietly replaced by a default. ## Extending the idea with converters `ConfigParser(converters={"list": lambda text: [part.strip() for part in text.split(",") if part.strip()]})` synthesises `cfg.getlist(section, option)` and `cfg["extract"].getlist(option)` from the name you supplied. That is the module's answer to "my config has a comma-separated list": one place that owns the parsing and raises on bad input, instead of a `split` scattered through the code. ## The contrast that makes this leaf make sense A TOML parser returns *typed* values — `true` is already a `bool`, `4` is already an `int`, so there is no coercion step and no place for this bug. INI has no types, so `configparser` reintroduces them at the accessor. Environment variables are in the same category as INI: always strings, always needing an explicit conversion with an explicit boolean vocabulary. Whenever configuration crosses a text boundary, the conversion has to be deliberate; `getboolean` is what deliberate looks like. A last subtlety worth carrying: `bool` is a subclass of `int` in Python, so any generic "coerce this string to the type of the existing value" helper must test `isinstance(value, bool)` *before* it tests `isinstance(value, int)`. Get that order wrong and `"0"` becomes `True` again by a different route.
- What does getboolean do with a value it does not recognise, such as 'maybe'?It raises `ValueError`. The accepted vocabulary is fixed — 1/yes/true/on and 0/no/false/off, case-insensitive — and anything outside it is an error rather than a guess. `fallback=` does not rescue it either: fallback covers a missing option, not an unparsable one.
- Does the same trap apply to environment variables?Exactly the same. `os.environ` values are always `str`, so `bool(os.environ.get("DEBUG"))` is true for `"false"` and `"0"` alike, and false only when the variable is missing or empty. Every boolean environment override needs an explicit word-to-bool mapping of its own.
- How would you add a typed accessor for a comma-separated list option?Pass `converters={"list": ...}` when constructing the parser; configparser then synthesises `getlist` on both the parser and every section proxy. It keeps the parsing and its error handling in one place instead of a `split` repeated at each call site.
saying these in an interview costs you the question
- Thinks bool('false') evaluates to False
- Believes configparser returns ints for numeric-looking values
- Expects getboolean to accept any word meaning yes
- Uses fallback= to paper over an unparsable value
- Calls int() on a value without handling ValueError
- Checks isinstance int before isinstance bool when coercing