https://github.com/python/cpython/commit/0f8a07d158ba0a0dfd3584d8195f30dd16738555
commit: 0f8a07d158ba0a0dfd3584d8195f30dd16738555
branch: main
author: Alex Waygood <[email protected]>
committer: AlexWaygood <[email protected]>
date: 2024-05-07T11:12:28Z
summary:

gh-118418: Deprecate failing to pass a value to the *type_params* parameter of 
some private `typing` APIs (#118695)

files:
A Misc/NEWS.d/next/Library/2024-05-07-11-23-11.gh-issue-118418.QPMdJm.rst
M Lib/test/test_typing.py
M Lib/typing.py

diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 8f0be1fd3f55e5..012613302d1b53 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -6308,6 +6308,31 @@ def test_or(self):
         self.assertEqual(X | "x", Union[X, "x"])
         self.assertEqual("x" | X, Union["x", X])
 
+    def test_deprecation_for_no_type_params_passed_to__evaluate(self):
+        with self.assertWarnsRegex(
+            DeprecationWarning,
+            (
+                "Failing to pass a value to the 'type_params' parameter "
+                "of 'typing._eval_type' is deprecated"
+            )
+        ) as cm:
+            self.assertEqual(typing._eval_type(list["int"], globals(), {}), 
list[int])
+
+        self.assertEqual(cm.filename, __file__)
+
+        f = ForwardRef("int")
+
+        with self.assertWarnsRegex(
+            DeprecationWarning,
+            (
+                "Failing to pass a value to the 'type_params' parameter "
+                "of 'typing.ForwardRef._evaluate' is deprecated"
+            )
+        ) as cm:
+            self.assertIs(f._evaluate(globals(), {}, 
recursive_guard=frozenset()), int)
+
+        self.assertEqual(cm.filename, __file__)
+
 
 @lru_cache()
 def cached_func(x, y):
diff --git a/Lib/typing.py b/Lib/typing.py
index c159fcfda68ee8..e48583673a7be2 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -437,13 +437,38 @@ def inner(*args, **kwds):
     return decorator
 
 
-def _eval_type(t, globalns, localns, type_params=None, *, 
recursive_guard=frozenset()):
+def _deprecation_warning_for_no_type_params_passed(funcname: str) -> None:
+    import warnings
+
+    depr_message = (
+        f"Failing to pass a value to the 'type_params' parameter "
+        f"of {funcname!r} is deprecated, as it leads to incorrect behaviour "
+        f"when calling {funcname} on a stringified annotation "
+        f"that references a PEP 695 type parameter. "
+        f"It will be disallowed in Python 3.15."
+    )
+    warnings.warn(depr_message, category=DeprecationWarning, stacklevel=3)
+
+
+class _Sentinel:
+    __slots__ = ()
+    def __repr__(self):
+        return '<sentinel>'
+
+
+_sentinel = _Sentinel()
+
+
+def _eval_type(t, globalns, localns, type_params=_sentinel, *, 
recursive_guard=frozenset()):
     """Evaluate all forward references in the given type t.
 
     For use of globalns and localns see the docstring for get_type_hints().
     recursive_guard is used to prevent infinite recursion with a recursive
     ForwardRef.
     """
+    if type_params is _sentinel:
+        _deprecation_warning_for_no_type_params_passed("typing._eval_type")
+        type_params = ()
     if isinstance(t, ForwardRef):
         return t._evaluate(globalns, localns, type_params, 
recursive_guard=recursive_guard)
     if isinstance(t, (_GenericAlias, GenericAlias, types.UnionType)):
@@ -1018,7 +1043,10 @@ def __init__(self, arg, is_argument=True, module=None, 
*, is_class=False):
         self.__forward_is_class__ = is_class
         self.__forward_module__ = module
 
-    def _evaluate(self, globalns, localns, type_params=None, *, 
recursive_guard):
+    def _evaluate(self, globalns, localns, type_params=_sentinel, *, 
recursive_guard):
+        if type_params is _sentinel:
+            
_deprecation_warning_for_no_type_params_passed("typing.ForwardRef._evaluate")
+            type_params = ()
         if self.__forward_arg__ in recursive_guard:
             return self
         if not self.__forward_evaluated__ or localns is not globalns:
@@ -2998,15 +3026,6 @@ def __new__(cls, typename, bases, ns):
         return nm_tpl
 
 
-class _Sentinel:
-    __slots__ = ()
-    def __repr__(self):
-        return '<sentinel>'
-
-
-_sentinel = _Sentinel()
-
-
 def NamedTuple(typename, fields=_sentinel, /, **kwargs):
     """Typed version of namedtuple.
 
diff --git 
a/Misc/NEWS.d/next/Library/2024-05-07-11-23-11.gh-issue-118418.QPMdJm.rst 
b/Misc/NEWS.d/next/Library/2024-05-07-11-23-11.gh-issue-118418.QPMdJm.rst
new file mode 100644
index 00000000000000..be371c507cbf79
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-05-07-11-23-11.gh-issue-118418.QPMdJm.rst
@@ -0,0 +1,6 @@
+A :exc:`DeprecationWarning` is now emitted if you fail to pass a value to
+the new *type_params* parameter of ``typing._eval_type()`` or
+``typing.ForwardRef._evaluate()``. (Using either of these private and
+undocumented functions is discouraged to begin with, but failing to pass a
+value to the ``type_params`` parameter may lead to incorrect behaviour on
+Python 3.12 or newer.)

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to