As Guido suggested, I've created a new draft PEP for "User-defined Type Guards". Input is welcome. Draft Proposal (in fork): https://github.com/erictraut/peps/blob/master/pep-9999.rst Draft Proposal (branch compare): https://github.com/python/peps/compare/master...erictraut:master
Thank you for the PEP, I really like it. A few remarks:
There is one use case (that might be out of scope for this PEP) that is not covered, but is quite useful. I call it "type assertions". For example "assert isinstance(foo, Bar)" already narrows "foo" in the following code. I'll give one real-life example that we use quite a lot:
def parse_foo_json_request(j: Any) -> None:
validate_foo_json(j)
# Now the shape of j is known and data can be safely
extracted.
def validate_foo_json(j: Any) -> None:
# Do various checks on j, raise a complex exception if is
doesn't
# match the expected shape.
pass
This can't be rewritten using a type guard, since the exception raised within the validation function depends on the exact problem with the given shape. This will later be used to give a detailed, structured explanation of went wrong to the caller of a web API. With the current PEP I would have to write this is something like this:
def parse_foo_json_request(j: Any) -> None:
if not validate_foo_json(j):
# dummy, will never happen
raise RuntimeError():
# Now the shape of j is known and data can be safely
extracted.
def validate_foo_json(j: Any) -> TypeGuard[Foo]:
# Do various checks on j, raise a complex exception if is
doesn't
# match the expected shape.
return True
One idea I have (not though through) is to be able to write something like this:
def validate_foo_json(j: TypeGuard[Foo]) -> None:
# Do various checks on j, raise a complex exception if is
doesn't
# match the expected shape.
return True
I.e. if the type guard is at an argument position, the validation function will not return if it doesn't match the type.
But as I said, this might be out of scope of the current PEP.
- Sebastian