-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
Description
Bug report
Bug description:
This report highlights a significant ergonomic and potential design issue arising from the interaction of PEP 649's lazy annotations and the typing.get_type_hints() function. This interaction introduces a behavior akin to a "come from" (a problematic programming primitive) for type resolution, violating the principle of least surprise and creating challenges for library authors relying on type introspection.
Problem:
With lazy annotations becoming the default, type hints within class definitions are no longer evaluated at definition time. Instead, they are stored as strings and resolved later using typing.get_type_hints(). This creates a problem when a class with type-hinted attributes referencing other locally defined classes is introspected by a decorator or other metaprogramming mechanism.
Consider the following minimal example:
def example_function():
class LocalType:
value: int = 1
class MyClass:
item: LocalType
import typing
hints = typing.get_type_hints(MyClass)
print(hints)
In this scenario, typing.get_type_hints(MyClass) will typically fail to resolve "LocalType" because the local namespace where LocalType is defined is not readily accessible.
This forces library authors, who often use decorators to introspect class structure and type hints (e.g., for serialization, validation, ORMs), to resort to fragile and discouraged techniques like inspecting the call stack (sys._getframe()) to attempt to access the correct local namespace.
Impact:
Violation of 'Least Surprise':
Developers expect that a type hint like item: LocalType within MyClass refers to the LocalType defined in the same scope. The fact that its resolution depends on the runtime context of where get_type_hints() is called (a "come from" for type resolution) is highly unexpected and makes code harder to reason about.
Increased Library Complexity & Fragility: Libraries relying on type introspection are forced to implement complex and potentially unreliable workarounds for a fundamental language feature change. This increases the maintenance burden and the likelihood of subtle bugs.
Breaks Existing Code: Code that worked correctly in older Python versions (where type hints were evaluated eagerly) can now fail or require significant modification.
Limits Design Patterns: This issue restricts the ability of libraries to cleanly work with data structures and types defined within local scopes, which can be a useful pattern for encapsulation and testing.
Proposed Solution (or Discussion Points):
Enhance typing.get_type_hints(): Consider adding a mechanism or options to get_type_hints() to more reliably resolve type hints in common metaprogramming scenarios, perhaps by providing access to the defining scope of the class.
Introduce a Standard Mechanism: Explore a new, officially supported way for decorators or metaclasses to access the necessary namespace information for resolving type hints.
Documentation Clarity: At a minimum, the documentation for typing and decorators should prominently address this issue and provide guidance (even if temporary) for library authors.
This issue represents more than just an ergonomic inconvenience. It introduces a non-local dependency for type resolution, breaking a fundamental principle of predictable code behavior.
CPython versions tested on:
3.14
Operating systems tested on:
Windows