GeezerVison is showing github.com — typecheck: support Protocol, TypedDict, Generic, ForwardRef (D4 set 3) · Issue #98 · Technologicat/unpythonic
typecheck: support Protocol, TypedDict, Generic, ForwardRef (D4 set 3) #98DescriptionContext
The runtime type checker (unpythonic.typecheck.isoftype) has been expanded in 2.0.0 to support many more typing features. The remaining unsupported features are the hard cases:
Remaining types
-
Protocol — full structural subtyping. Would need to inspect which methods/attributes the protocol requires and check that the value's class provides them. Heavy to implement correctly, and typing.runtime_checkable already provides a partial solution for protocols that opt in.
-
TypedDict — need to check required vs. optional keys, and verify value types per-key. Medium difficulty. The metadata is available via __required_keys__, __optional_keys__, and __annotations__.
-
Generic — abstract; unclear what "value matches Generic[T]" means at runtime. Possibly not meaningful for isoftype at all.
-
ForwardRef — a string reference to a type that must be resolved in some namespace. Would need the caller to supply a namespace (or use the caller's frame globals), adding API complexity.
Discussion
These are listed roughly in order of potential usefulness for unpythonic.dispatch. Protocol and TypedDict have concrete use cases; Generic and ForwardRef are more questionable.
For Protocol, it may be worth checking whether typing.runtime_checkable + isinstance is sufficient, rather than reimplementing structural subtyping.
Other typing features that were missing pre-2.0.0 were completed in commits 665cc4b through 6aa72ea. ActivityTechnologicat commented on Mar 12, 2026 TypedDict and Protocol are now implemented:
- TypedDict: Structural checking — required/optional keys via
__required_keys__/__optional_keys__, value types via get_type_hints(), recursive isoftype. Supports total=False, inheritance, compound value types.
- Protocol:
@runtime_checkable Protocols delegate to isinstance. Non-runtime-checkable Protocols raise TypeError with actionable message. Detection uses _is_protocol attribute (not issubclass, which gives false positives on 3.10).
Remaining from this issue:
- Generic — still unclear what "value matches
Generic[T]" means at runtime. Likely not meaningful for isoftype.
- ForwardRef — requires caller-supplied namespace. Not implemented.
Technologicat commented on Mar 12, 2026 On _is_protocol, Claude Code says:
Clarification on Protocol detection: _is_protocol is used instead of issubclass(T, Protocol) because the latter gives a false positive for int on Python 3.10 (fixed in 3.11+). _is_protocol is set by typing internally and is stable across all supported versions. Not worth a version-gated code path for a one-line check that already works everywhere.
A bit cringy to abuse Python internals like that, but it's not like it's the first time unpythonic.typecheck does that, so going with this solution for now. May later migrate to use public API, if a way to check "is this a protocol" becomes available there.
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|