Add is_type utility

This commit is contained in:
thatmattlove 2021-09-13 02:39:11 -07:00
parent bf17ec0c95
commit bcf339f78f
3 changed files with 94 additions and 0 deletions

View file

@ -0,0 +1 @@
"""hyperglass.util tests."""

View file

@ -0,0 +1,66 @@
"""Test typing utilities."""
# flake8: noqa
# Standard Library
import typing
# Local
from ..typing import is_type
class EmptyTestClass:
pass
class EmptySubClass(EmptyTestClass):
pass
_string = "Test String"
_string_empty = ""
_dict = {"one": 1, "two": 2}
_dict_empty = dict()
_list = [1, 2, 3]
_list_empty = []
_set = {"one", "two"}
_set_empty = set()
_tuple = (1, 2, 3)
_tuple_empty = tuple()
_class = EmptyTestClass
_class_instance = EmptyTestClass()
_subclass = EmptySubClass
_subclass_instance = EmptySubClass()
DictOrString = typing.Union[typing.Dict, str]
ClassOrString = typing.Union[EmptyTestClass, str]
def test_is_type():
checks = (
("Non-Empty String is String", True, _string, str),
("Empty String is String", True, _string_empty, str),
("Non-Empty Dict is Dict", True, _dict, typing.Dict),
("Empty Dict is Dict", True, _dict_empty, dict),
("Non-Empty List is List", True, _list, typing.List),
("Empty List is List", True, _list_empty, list),
("Non-Empty Tuple is Tuple", True, _tuple, typing.Tuple),
("Empty Tuple is Tuple", True, _tuple_empty, tuple),
("Non-Empty Set is Set", True, _set, typing.Set),
("Empty Set is Set", True, _set_empty, set),
("Non-Empty String is Dict or String", True, _string, DictOrString),
("Non-Empty Dict is Dict or String", True, _dict, DictOrString),
("Non-Empty List is Dict or String", False, _list, DictOrString),
("Empty list is Dict or String", False, _list_empty, DictOrString),
("Class object is Class object", False, _class, _class),
("Class instance is Class instance", True, _class_instance, _class_instance),
("Class instance is Class object", True, _class_instance, _class),
("Subclass instance is Class instance", True, _subclass_instance, _class_instance),
("Subclass instance is Class object", True, _subclass_instance, _class),
("Class object is Class or String", False, _subclass, ClassOrString),
("Class instance is Class or String", True, _class_instance, ClassOrString),
("Subclass instance is Class or String", True, _subclass_instance, ClassOrString),
)
for _, expected, value, _type in checks:
result = is_type(value, _type)
if result is not expected:
raise AssertionError(f"Got `{value}`, expected `{str(_type)}`")

27
hyperglass/util/typing.py Normal file
View file

@ -0,0 +1,27 @@
"""Typing utilities."""
# Standard Library
import typing
import inspect
def is_type(value: typing.Any, *types: typing.Any) -> bool:
"""Verify if the type of `value` matches any provided type in `types`.
Will only check the main type for generics like `Dict` or `List`, but will check the individual
types of generics like `Union` or `Optional`.
Probably wrong, but seems to work for most cases.
"""
for _type in types:
if _type is None:
return value is None
if inspect.isclass(_type):
return isinstance(value, _type)
origin = typing.get_origin(_type)
if origin is typing.Union:
return any(is_type(value, t) for t in _type.__args__)
if origin is None:
return isinstance(value, type(_type))
return isinstance(value, origin)
return False