Proposal for assure: A Built-in Keyword for Simplified Non-None Type Validation
Problem Statement
When functions return values of type Optional (e.g., int | None), developers must add boilerplate to ensure the returned value is non-None before usage. This often involves custom TypeGuards, inline checks, or assertions, which:
- Reduce readability by adding repetitive code.
- Increase error-prone manual handling of
Nonevalues. - Force developers to use constructs like
assert, which are unsuitable for production environments.
Proposed Solution
Introduce the assure keyword, designed to validate that a value is non-None, narrow its type, and raise an exception if the value is None. For example:
def fetch_data() -> int | None:
...
result = assure fetch_data()
# `result` is guaranteed to be `int` at this point, or an exception is raised.
How It Works
- Runtime Validation: If the return value is
None,assureraises an exception (ValueErroror a new specific exception type). - Type Narrowing: The type of the value is narrowed to exclude
None, leveraging Python’s type hints and static analysis tools.
Advantages
- Improved Readability: Removes boilerplate and provides a declarative syntax for non-
Nonevalidation. - Safety in Production: Unlike
assert,assurealways validates at runtime, ensuring robust error handling. - Alignment with Typing Standards: Works seamlessly with Python’s type system and enhances static analysis capabilities.
Implementation in Python
A simple implementation to emulate the assure keyword:
from typing import TypeVar, NoReturn
T = TypeVar("T")
def assure(value: T | None) -> T:
if value is None:
raise ValueError("Expected a non-None value, got None.")
return value
Usage:
def fetch_data() -> int | None:
return None # Example returning None
result = assure(fetch_data()) # Raises ValueError
Underlying C-Level Implementation
A possible implementation for CPython, demonstrating how assure could be built into Python’s runtime (in pseudocode):
PyObject* assure(PyObject* obj) {
if (obj == Py_None) {
PyErr_SetString(PyExc_ValueError, "Expected a non-None value, got None.");
return NULL;
}
Py_INCREF(obj); // Increment reference count for the returned value
return obj;
}
How It Works:
- The
assurefunction is added to CPython’s parser as a new keyword. - It checks if the provided
PyObject*isPy_Noneand raises aValueErrorif true. - Otherwise, it increments the reference count and returns the value, ensuring it is safe to use.
Community Impact
- Tooling Updates: Linters and type-checking tools like
mypywill require updates to support the new keyword, which is standard for new PEPs. - Backward Compatibility:
assuredoes not conflict with existing syntax, ensuring no disruption to current codebases.
Would love feedback from the community on this proposal, including additional use cases, potential challenges, or other considerations.