extract_type

privex.helpers.common.extract_type(tp: Union[type, callable, object], **kwargs) → Optional[Union[type, object, callable, tuple, Tuple[type]]][source]

Attempt to identify the type of a given value, or for functions/methods - identify their RETURN value type.

This function can usually detect typing types, including generics such as List[str], and will attempt to extract their native Python base type, e.g. list.

For typing.Union based types (including typing.Optional), it can extract a tuple of base types, including from nested typing.Union’s - e.g. Union[str, list, Union[dict, set], int would be simplified down to (str, list, dict, set, int)

Attention

If you want to extract the original return type from a function/method, including generic types such as List[str], then you should use get_return_type() instead.

Example 1 - convert a generic type e.g. Dict[str, str] into it’s native type (e.g. dict):

>>> dtype = Dict[str, str]
>>> # noinspection PyTypeHints,PyTypeChecker
>>> isinstance({}, dtype)
TypeError: Subscripted generics cannot be used with class and instance checks
>>> extract_type(dtype)
dict
>>> isinstance({}, extract_type(dtype))
True

Example 2 - extract the return type of a function/method, and if the return type is a generic (e.g. List[str]), automatically convert it into the native type (e.g. list) for use in comparisons such as isinstance():

>>> def list_wrap(v: T) -> List[T]:
...     return [v]
>>>
>>> extract_type(list_wrap)
list
>>> isinstance([1, 2, 3], extract_type(list_wrap))
True

Example 3 - extract the type from an instantiated object, allowing for isinstance() comparisons:

>>> from privex.helpers import DictObject
>>> db = DictObject(hello='world', lorem='ipsum')
{'hello': 'world', 'lorem': 'ipsum'}
>>> type_db = extract_type(db)
privex.helpers.collections.DictObject
>>> isinstance(db, type_db)
True
>>> isinstance(DictObject(test=123), type_db)
True

Example 4 - extract a tuple of types from a typing.Union or typing.Optional (inc. return types)

>>> def hello(x) -> Optional[str]:
...     return x * 5
...
>>> extract_type(hello)
(str, NoneType)
>>> # Even with a Union[] containing a List[], another Union[] (containing a Tuple and set), and a Dict[],
>>> # extract_type is still able to recursively flatten and simplify it down to a tuple of base Python types
>>> extract_type(Union[
...     List[str],
...     Union[Tuple[str, int, str], set],
...     Dict[int, str]
... ])
(list, tuple, set, dict)

Return Types

A type will be returned for most calls where tp is either:

  • Already a native type e.g. list

  • A generic type such as List[str] (which are technically instances of object)

  • A function/method with a valid return type annotation, including generic return types

  • An instance of a class (an object), where the original type can be easily extracted via tp.__class__

If tp was an object and the type/class couldn’t be extracted, then it would be returned in it’s original object form.

If tp was an unusual function/method which couldn’t be detected as one, or issues occurred while extracting the return type, then tp may be returned in it’s original callable form.

Parameters

tp – The type/object/function etc. to extract the most accurate type from

Return type|object|callable ret

A type will be returned for most calls, but may be an object or callable if there were issues detecting the type.