I have to check an object that may have been created by an API.
When I try using isinstance(obj, MyClass) it get a TypeError if obj was created by the API.
I wrote a custom function to handle this.
def is_instance(obj: Any, class_or_tuple: Any) -> bool:
try:
return isinstance(obj, class_or_tuple)
except TypeError:
return False
The issue I am having is using is_instance() instead of the builtin isinstance() does not have any TypeGuard support, so the type checker complains.
def my_process(api_obj: int | str) -> None:
if is_instance(api_obj, int):
process_int(api_obj)
else:
process_str(api_obj)
"Type int | str cannot be assigned to parameter ..."
How could I create a TypeGuard for this function?
You can annotate
is_instancewith aTypeGuardthat narrows the type to that of the second argument. To handle a tuple of types or such tuples asclass_or_tuple, use a type alias that allows either a type or a tuple of the type alias itself:But then, as @user2357112 points out in the comment,
TypeGuardisn't just meant for narrowing by type, but also value, so failing a check ofis_instance(api_obj, int)doesn't mean to the type checker thatapi_objis necessarilystr, so using anelseclause would not work:so in this case you would have to work around it with a redundant call of
is_instance(api_obj, str):Demo with mypy: https://mypy-play.net/?mypy=latest&python=3.12&gist=4cea456751dff62c3e0bc998b74462f5
Demo of type narrowing with a tuple of types with mypy: https://mypy-play.net/?mypy=latest&python=3.12&gist=98ca0795a315e541c4b1b9376d81812f
EDIT: For PyRight as you requested in the comment, you would have to make the type alias generic:
This would make mypy complain, however, so it's really down to different type checkers having different interpretations to the Python typing rules.
Demo with PyRight here