mrkeyoor.com_
Sun 20 Sept 17:54 UTC
PyPIUtilsupdated 13 Sept 2026

typing-inspect review

Our typing-inspect 0.9.0 install exposed predicates and accessors for runtime typing objects: unions, optionals, literals, TypeVars, NewType wrappers, forward references, generic bases, bounds, constraints, origins, and arguments. It was built when Python's typing internals varied sharply across releases, and it still covers a few questions that typing.get_origin and typing.get_args do not answer directly. Version 0.9.0 fixed handling of unsubscripted generics on Python 3.9 and newer and adjusted to a typing_extensions change. It was published in May 2023, remains classified Alpha, declares no Python requirement, and has no py.typed marker despite being a typing utility.

Verdict

Do not add typing-inspect merely for get_origin and get_args on a current Python project. Keep it for its few unique legacy predicates only after testing PEP 604 unions, Annotated, TypedDict, postponed annotations, and each supported interpreter against version 0.9.0.

We installed it

Lab card: what happened when we installed typing-inspectScreenshot of typing-inspect documentation
Install✓ · 0.2s3 packages on disk · 1 MB
Importimport typing_inspect in 0.12s · pure Python
Known vulns0(pip-audit)

Answers from our run

Does typing-inspect install cleanly?

Yes. In a fresh container with an empty cache, pip install typing-inspect finished in 0.2s, leaving 3 packages and 1 MB on disk. pip-audit reported no known vulnerabilities.

What does typing-inspect need to run?

Python 3.x, and nothing compiled: it is pure Python. In our run import typing_inspect succeeded in 0.12s.

typing-inspect or typing-extensions: which should you use?

typing-extensions: Choose it to use and backport newer typing constructs while relying on standard-library get_origin and get_args for inspection. Do not add typing-inspect merely for get_origin and get_args on a current Python project.

When should you not use typing-inspect?

You only need origin and arguments on Python 3.8 or newer. typing.get_origin and typing.get_args are in the standard library and understand newer forms better

API stability3/5The named helper surface has changed little, and version 0.9.0 mainly corrected compatibility rather than renaming calls. Behavior is less stable than the function list suggests because the package reads objects whose internal representation changes with CPython and typing_extensions. The same release returns an origin for typing.Union but None for a PEP 604 union, and get_parameters intentionally differs across Python 3.8 and 3.9 for unsubscripted List.
Docs2/5The README states the experimental status, gives one generic-type example, and lists each public function with a short description. Most working examples are left in docstrings and there is no compatibility table by Python version. It does not warn README readers about current Annotated metadata loss, standard-library TypedDict detection, PEP 604 origin behavior, the missing py.typed marker, or the modern exceptions from get_last_origin and get_last_args.
Maintenance2/5PyPI 0.9.0 was uploaded on 24 May 2023 and remains the current release. The repository pushed on 18 January 2026, so it is not completely untouched, but those later source changes are unavailable to ordinary pinned installs. GitHub showed 24 open issues and pull requests, and PyPI still labels the project Alpha. For an interpreter-sensitive utility, a three-year gap between published compatibility updates is a material risk.
Ecosystem3/5Weekly use is recorded at 20,513,124 downloads, yet GitHub showed only 375 stars. That mismatch points to a package pulled by established dependency trees rather than a large direct-user community. mypy-extensions and typing-extensions connect it to older Python typing infrastructure, while modern libraries often use the standard library, typing_extensions, or their own compatibility code for newer annotation forms.

Use it if

  • An existing compatibility layer already depends on helpers such as is_optional_type, is_new_type, get_bound, get_constraints, or get_generic_bases
  • Your serializer or dependency injector supports older annotation shapes and has tests for every Python version it claims
  • You must inspect classic typing.Optional, typing.Union, Literal, TypeVar, NewType, or Generic forms through one legacy API
  • The package is already transitive and one of its unique predicates removes more compatibility code than adopting it adds
Skip it if

Setup reality

We installed typing-inspect 0.9.0 in a fresh Python 3.12 Bookworm container with no cache. uv completed in 0.2 seconds and left three packages using 1 MB on disk. The measurement reported three direct dependencies. The code is pure Python, and its package metadata does not specify a Python requirement. pip-audit found zero known vulnerabilities. import typing_inspect succeeded in 0.12 seconds. The installed distribution has an MIT license and no py.typed marker.

The distribution is named typing-inspect, while Python imports typing_inspect. Its declared dependencies include mypy-extensions and typing-extensions, plus a conditional typing backport for very old Python. Modern static checkers cannot treat the installed module as a typed package because it publishes neither inline typed-package metadata nor bundled stubs. You may need a missing-import override or local stub. No native compiler, environment variable, credential, or configuration file is involved.

Compatibility is the real setup burden. get_last_origin and get_last_args deliberately raise ValueError on modern Python and tell callers to use get_origin or get_args. The 0.9.0 implementation recognizes PEP 604 unions in is_union_type and get_args, yet get_origin only checks typing's generic alias class, so an int | str origin falls through to None. Annotated metadata and standard-library TypedDict detection have similar gaps. Test concrete annotations on every interpreter in your support matrix rather than assuming a helper name covers later typing features.

Use typing.get_type_hints before inspection when postponed annotations or string forward references are possible. That call may execute code contained in annotations, so do not run it on untrusted definitions. get_args only unwraps one level, and is_optional_type checks direct None membership rather than recursively resolving TypeVars. Version 0.9.0 also changed get_parameters for unsubscripted List on Python 3.9 and later to return an empty tuple because newer aliases do not retain the earlier parameter data.

Patterns

Check for a direct optional type detect-optional

from typing import Optional
from typing_inspect import get_args, is_optional_type

annotation = Optional[str]
if is_optional_type(annotation):
    value_types = tuple(arg for arg in get_args(annotation) if arg is not type(None))

The predicate checks direct None membership. It does not recursively expand a TypeVar bound to an optional or an optional nested inside a list.

Read classic Union members inspect-union

from typing import Union
from typing_inspect import get_args, get_origin, is_union_type

annotation = Union[int, str]
assert is_union_type(annotation)
assert get_args(annotation) == (int, str)
print(get_origin(annotation))

This works for typing.Union. For int | str in 0.9.0, is_union_type and get_args work while get_origin returns None.

Handle a pipe union without its origin inspect-pep604-union

from typing_inspect import get_args, is_union_type

annotation = int | str
if is_union_type(annotation):
    members = get_args(annotation)
    print(members)

Dispatch on is_union_type rather than get_origin for PEP 604 syntax. The published 0.9.0 origin helper does not recognize types.UnionType.

Get a container and its parameters unwrap-generic

from typing import Dict, List
from typing_inspect import get_args, get_origin

annotation = Dict[str, List[int]]
container = get_origin(annotation)
key_type, value_type = get_args(annotation)
item_type = get_args(value_type)[0]

get_args unwraps one level at a time. Recurse explicitly when nested generic parameters matter.

Extract values from Literal read-literal-values

from typing import Literal
from typing_inspect import get_args, is_literal_type

Status = Literal["queued", "done"]
if is_literal_type(Status):
    allowed = frozenset(get_args(Status))

Literal arguments are values, not classes. Compare them directly and do not pass them into isinstance.

Read TypeVar bounds and constraints inspect-typevar

from typing import TypeVar
from typing_inspect import get_bound, get_constraints, is_typevar

Number = TypeVar("Number", bound=int)
Text = TypeVar("Text", str, bytes)

assert is_typevar(Number)
print(get_bound(Number))
print(get_constraints(Text))

A TypeVar uses a bound or a constraint tuple. Calling these helpers with a non-TypeVar raises TypeError.

Find the base behind NewType inspect-newtype

from typing import NewType
from typing_inspect import is_new_type

UserId = NewType("UserId", int)
base = UserId.__supertype__ if is_new_type(UserId) else UserId

NewType changed representation in Python 3.10. is_new_type covers the older callable and newer class-based forms supported by 0.9.0.

Read a ForwardRef target inspect-forward-reference

from typing import ForwardRef
from typing_inspect import get_forward_arg, is_forward_ref

reference = ForwardRef("Account")
if is_forward_ref(reference):
    print(get_forward_arg(reference))

Postponed annotations can remain plain strings instead of ForwardRef objects. Resolve trusted annotations with typing.get_type_hints before inspection.

Recover an instance generic when available inspect-generic-instance

from typing import Generic, TypeVar
from typing_inspect import get_generic_type

T = TypeVar("T")
class Box(Generic[T]):
    pass

parameterized = Box[int]()
plain = Box()
print(get_generic_type(parameterized))
print(get_generic_type(plain))

A parameterized construction may set __orig_class__. A plain Box instance only reveals its runtime class, so element type information is unavailable.

Read original generic base classes inspect-generic-bases

from typing import Generic, TypeVar
from typing_inspect import get_generic_bases

T = TypeVar("T")
class Parent(Generic[T]):
    pass
class Child(Parent[int]):
    pass

print(get_generic_bases(Child))

The result comes from __orig_bases__ on current Python and may be empty when a class or metaclass does not retain that information.

Use the standard library for Annotated avoid-annotated-loss

from typing import Annotated, get_args, get_origin

Field = Annotated[int, "max=10"]
assert get_origin(Field) is Annotated
base_type, *metadata = get_args(Field)

typing_inspect 0.9.0 unwraps Annotated to int and drops its metadata. Use typing.get_origin and typing.get_args for this construct.

Inspect a standard TypedDict without typed_dict_keys inspect-typed-dict

from typing import NotRequired, TypedDict, get_type_hints

class UserRow(TypedDict):
    id: int
    nickname: NotRequired[str]

fields = get_type_hints(UserRow, include_extras=True)
required = UserRow.__required_keys__
optional = UserRow.__optional_keys__

typed_dict_keys in 0.9.0 misses current typing.TypedDict classes. The standard attributes also preserve which keys are required.

Alternatives

PackageRegistryPick it when
typing-extensionsPyPIChoose it to use and backport newer typing constructs while relying on standard-library get_origin and get_args for inspection
beartypePyPIChoose it when the goal is runtime enforcement of annotations rather than building your own dispatcher from inspection predicates
typeguardPyPIChoose it for decorator or import-hook runtime checking with errors that identify the mismatched value and annotation
pydanticPyPIChoose it when annotation inspection is only a step toward input validation, coercion, serialization, and schema generation

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.