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
None
values. - 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
,assure
raises an exception (ValueError
or 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-
None
validation. - Safety in Production: Unlike
assert
,assure
always 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
assure
function is added to CPython’s parser as a new keyword. - It checks if the provided
PyObject*
isPy_None
and raises aValueError
if 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
mypy
will require updates to support the new keyword, which is standard for new PEPs. - Backward Compatibility:
assure
does 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.