diff --git a/CHANGELOG.md b/CHANGELOG.md index cfb45718..ba1a6d78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ - Drop support for Python 3.8 (including PyPy-3.8). Patch by [Victorien Plot](https://github.com/Viicos). - Do not attempt to re-export names that have been removed from `typing`, anticipating the removal of `typing.no_type_check_decorator` in Python 3.15. + Patch by Jelle Zijlstra. +- Update `typing_extensions.Format` and `typing_extensions.evaluate_forward_ref` to align + with changes in Python 3.14. Patch by Jelle Zijlstra. +- Fix tests for Python 3.14. Patch by Jelle Zijlstra. New features: @@ -10,7 +14,6 @@ New features: Patch by [Victorien Plot](https://github.com/Viicos). - Add `typing_extensions.Reader` and `typing_extensions.Writer`. Patch by Sebastian Rittau. -- Fix tests for Python 3.14. Patch by Jelle Zijlstra. # Release 4.13.2 (April 10, 2025) diff --git a/doc/index.rst b/doc/index.rst index 325182eb..68402faf 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -769,7 +769,7 @@ Functions .. versionadded:: 4.2.0 -.. function:: evaluate_forward_ref(forward_ref, *, owner=None, globals=None, locals=None, type_params=None, format=Format.VALUE) +.. function:: evaluate_forward_ref(forward_ref, *, owner=None, globals=None, locals=None, type_params=None, format=None) Evaluate an :py:class:`typing.ForwardRef` as a :py:term:`type hint`. @@ -796,7 +796,7 @@ Functions This parameter must be provided (though it may be an empty tuple) if *owner* is not given and the forward reference does not already have an owner set. *format* specifies the format of the annotation and is a member of - the :class:`Format` enum. + the :class:`Format` enum, defaulting to :attr:`Format.VALUE`. .. versionadded:: 4.13.0 @@ -952,9 +952,19 @@ Enums for the annotations. This format is identical to the return value for the function under earlier versions of Python. + .. attribute:: VALUE_WITH_FAKE_GLOBALS + + Equal to 2. Special value used to signal that an annotate function is being + evaluated in a special environment with fake globals. When passed this + value, annotate functions should either return the same value as for + the :attr:`Format.VALUE` format, or raise :exc:`NotImplementedError` + to signal that they do not support execution in this environment. + This format is only used internally and should not be passed to + the functions in this module. + .. attribute:: FORWARDREF - Equal to 2. When :pep:`649` is implemented, this format will attempt to return the + Equal to 3. When :pep:`649` is implemented, this format will attempt to return the conventional Python values for the annotations. However, if it encounters an undefined name, it dynamically creates a proxy object (a ForwardRef) that substitutes for that value in the expression. @@ -964,7 +974,7 @@ Enums .. attribute:: STRING - Equal to 3. When :pep:`649` is implemented, this format will produce an annotation + Equal to 4. When :pep:`649` is implemented, this format will produce an annotation dictionary where the values have been replaced by strings containing an approximation of the original source code for the annotation expressions. diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index 333b4867..a7953dc5 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -29,7 +29,6 @@ from _typed_dict_test_helper import Foo, FooGeneric, VeryAnnotated from typing_extensions import ( _FORWARD_REF_HAS_CLASS, - _PEP_649_OR_749_IMPLEMENTED, Annotated, Any, AnyStr, @@ -8533,7 +8532,7 @@ def test_stock_annotations_in_module(self): get_annotations(isa.MyClass, format=Format.STRING), {"a": "int", "b": "str"}, ) - mycls = "MyClass" if _PEP_649_OR_749_IMPLEMENTED else "inspect_stock_annotations.MyClass" + mycls = "MyClass" if sys.version_info >= (3, 14) else "inspect_stock_annotations.MyClass" self.assertEqual( get_annotations(isa.function, format=Format.STRING), {"a": "int", "b": "str", "return": mycls}, @@ -8581,7 +8580,7 @@ def test_stock_annotations_on_wrapper(self): get_annotations(wrapped, format=Format.FORWARDREF), {"a": int, "b": str, "return": isa.MyClass}, ) - mycls = "MyClass" if _PEP_649_OR_749_IMPLEMENTED else "inspect_stock_annotations.MyClass" + mycls = "MyClass" if sys.version_info >= (3, 14) else "inspect_stock_annotations.MyClass" self.assertEqual( get_annotations(wrapped, format=Format.STRING), {"a": "int", "b": "str", "return": mycls}, diff --git a/src/typing_extensions.py b/src/typing_extensions.py index cf0427f3..1ab6220d 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -3821,27 +3821,15 @@ def __eq__(self, other: object) -> bool: __all__.append("CapsuleType") -# Using this convoluted approach so that this keeps working -# whether we end up using PEP 649 as written, PEP 749, or -# some other variation: in any case, inspect.get_annotations -# will continue to exist and will gain a `format` parameter. -_PEP_649_OR_749_IMPLEMENTED = ( - hasattr(inspect, 'get_annotations') - and inspect.get_annotations.__kwdefaults__ is not None - and "format" in inspect.get_annotations.__kwdefaults__ -) - - -class Format(enum.IntEnum): - VALUE = 1 - VALUE_WITH_FAKE_GLOBALS = 2 - FORWARDREF = 3 - STRING = 4 - - -if _PEP_649_OR_749_IMPLEMENTED: - get_annotations = inspect.get_annotations +if sys.version_info >= (3,14): + from annotationlib import Format, get_annotations else: + class Format(enum.IntEnum): + VALUE = 1 + VALUE_WITH_FAKE_GLOBALS = 2 + FORWARDREF = 3 + STRING = 4 + def get_annotations(obj, *, globals=None, locals=None, eval_str=False, format=Format.VALUE): """Compute the annotations dict for an object. @@ -4122,7 +4110,7 @@ def evaluate_forward_ref( globals=None, locals=None, type_params=None, - format=Format.VALUE, + format=None, _recursive_guard=frozenset(), ): """Evaluate a forward reference as a type hint.