Distinguish KeyError from a missing optional value
Also published in our primary archive.
To distinguish KeyError from a missing optional value, use membership to make the presence decision before reading a mapping value. The outer configuration contains {"option": None} but no "limit". As the first contrast shows, config.get("option") and config.get("limit") both return None; that result alone cannot say whether the optional value was explicitly supplied as null or the key is absent.
This experiment adds a separate required-key boundary. read_config first classifies the optional option with membership, preserving its present-null state. It then indexes the required limit inside a tightly scoped try block. The absent limit takes the KeyError branch and is converted into the domain result missing-required: limit. Thus the code distinguishes two outcomes that both looked like None through get(): a deliberately present optional null and a missing required setting. The assertions cover each branch and stdout is fixed.
Membership does not validate whether None is acceptable; the schema decides that. Likewise, converting KeyError here is appropriate only because limit is a named required configuration field. Avoid a broad except KeyError around unrelated work, since it could hide a missing key from another mapping operation. Dictionary membership, indexing, and get() are standard Python 3 dictionary operations with no special newer API version requirement.
Python documentation: Mapping Types — dict
AI assistance disclosure: This synthetic example and explanation were prepared with AI assistance.
def classify_optional(mapping, key):
if key in mapping:
return "present-null" if mapping[key] is None else "present-value"
return "missing"
def read_config(config):
option_state = classify_optional(config, "option")
try:
limit = config["limit"]
except KeyError:
return option_state, "missing-required: limit"
return option_state, f"limit: {limit}"
config = {"option": None}
assert config.get("option") is None
assert config.get("limit") is None
option_state, limit_state = read_config(config)
assert option_state == "present-null"
assert limit_state == "missing-required: limit"
print(f"option: {option_state}")
print(f"limit: {limit_state}")
option: present-null
limit: missing-required: limit