Skip to content

Commit 4773f27

Browse files
authored
Docs: Add a recipe for robust runtime introspection (#225)
1 parent 4fcf36a commit 4773f27

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed

doc/index.rst

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,57 @@ the risk of compatibility issues:
7373
attributes directly. If some information is not available through a public
7474
attribute, consider opening an issue in CPython to add such an API.
7575

76+
Here is an example recipe for a general-purpose function that could be used for
77+
reasonably performant runtime introspection of typing objects. The function
78+
will be resilient against any potential changes in ``typing_extensions`` that
79+
alter whether an object is reimplemented in ``typing_extensions``, rather than
80+
simply being re-exported from the :mod:`typing` module::
81+
82+
import functools
83+
import typing
84+
import typing_extensions
85+
from typing import Tuple, Any
86+
87+
# Use an unbounded cache for this function, for optimal performance
88+
@functools.lru_cache(maxsize=None)
89+
def get_typing_objects_by_name_of(name: str) -> Tuple[Any, ...]:
90+
result = tuple(
91+
getattr(module, name)
92+
# You could potentially also include mypy_extensions here,
93+
# if your library supports mypy_extensions
94+
for module in (typing, typing_extensions)
95+
if hasattr(module, name)
96+
)
97+
if not result:
98+
raise ValueError(
99+
f"Neither typing nor typing_extensions has an object called {name!r}"
100+
)
101+
return result
102+
103+
104+
# Use a cache here as well, but make it a bounded cache
105+
# (the default cache size is 128)
106+
@functools.lru_cache()
107+
def is_typing_name(obj: object, name: str) -> bool:
108+
return any(obj is thing for thing in get_typing_objects_by_name_of(name))
109+
110+
Example usage::
111+
112+
>>> import typing, typing_extensions
113+
>>> from functools import partial
114+
>>> from typing_extensions import get_origin
115+
>>> is_literal = partial(is_typing_name, name="Literal")
116+
>>> is_literal(typing.Literal)
117+
True
118+
>>> is_literal(typing_extensions.Literal)
119+
True
120+
>>> is_literal(typing.Any)
121+
False
122+
>>> is_literal(get_origin(typing.Literal[42]))
123+
True
124+
>>> is_literal(get_origin(typing_extensions.Final[42]))
125+
False
126+
76127
Python version support
77128
----------------------
78129

0 commit comments

Comments
 (0)