Skip to content

Commit fac7e89

Browse files
authored
Merge branch 'main' into test/executor-map-ref-cycles
2 parents 548d517 + ac14d4a commit fac7e89

File tree

79 files changed

+8587
-517
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+8587
-517
lines changed

Doc/library/annotationlib.rst

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,6 @@ Classes
204204
means may not have any information about their scope, so passing
205205
arguments to this method may be necessary to evaluate them successfully.
206206

207-
.. important::
208-
209-
Once a :class:`~ForwardRef` instance has been evaluated, it caches
210-
the evaluated value, and future calls to :meth:`evaluate` will return
211-
the cached value, regardless of the parameters passed in.
212-
213207
.. versionadded:: 3.14
214208

215209

Doc/library/typing.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3449,7 +3449,9 @@ Introspection helpers
34493449
.. versionadded:: 3.7.4
34503450

34513451
.. versionchanged:: 3.14
3452-
This is now an alias for :class:`annotationlib.ForwardRef`.
3452+
This is now an alias for :class:`annotationlib.ForwardRef`. Several undocumented
3453+
behaviors of this class have been changed; for example, after a ``ForwardRef`` has
3454+
been evaluated, the evaluated value is no longer cached.
34533455

34543456
.. function:: evaluate_forward_ref(forward_ref, *, owner=None, globals=None, locals=None, type_params=None, format=annotationlib.Format.VALUE)
34553457

Doc/whatsnew/3.14.rst

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ This example shows how these formats behave:
209209
...
210210
NameError: name 'Undefined' is not defined
211211
>>> get_annotations(func, format=Format.FORWARDREF)
212-
{'arg': ForwardRef('Undefined')}
212+
{'arg': ForwardRef('Undefined', owner=<function func at 0x...>)}
213213
>>> get_annotations(func, format=Format.STRING)
214214
{'arg': 'Undefined'}
215215

@@ -455,6 +455,13 @@ Other language changes
455455
The testbed can also be used to run the test suite of projects other than
456456
CPython itself. (Contributed by Russell Keith-Magee in :gh:`127592`.)
457457

458+
* Add a built-in implementation for HMAC (:rfc:`2104`) using formally verified
459+
code from the `HACL* <https://github.com/hacl-star/hacl-star/>`__ project.
460+
This implementation is used as a fallback when the OpenSSL implementation
461+
of HMAC is not available.
462+
(Contributed by Bénédikt Tran in :gh:`99108`.)
463+
464+
458465
.. _whatsnew314-pep765:
459466

460467
PEP 765: Disallow return/break/continue that exit a finally block
@@ -464,6 +471,7 @@ The compiler emits a :exc:`SyntaxWarning` when a :keyword:`return`, :keyword:`br
464471
:keyword:`continue` statements appears where it exits a :keyword:`finally` block.
465472
This change is specified in :pep:`765`.
466473

474+
467475
New modules
468476
===========
469477

@@ -705,6 +713,14 @@ graphlib
705713
(Contributed by Daniel Pope in :gh:`130914`)
706714

707715

716+
hmac
717+
----
718+
719+
* Add a built-in implementation for HMAC (:rfc:`2104`) using formally verified
720+
code from the `HACL* <https://github.com/hacl-star/hacl-star/>`__ project.
721+
(Contributed by Bénédikt Tran in :gh:`99108`.)
722+
723+
708724
http
709725
----
710726

@@ -1059,6 +1075,54 @@ turtle
10591075
(Contributed by Marie Roald and Yngve Mardal Moe in :gh:`126350`.)
10601076

10611077

1078+
types
1079+
-----
1080+
1081+
* :class:`types.UnionType` is now an alias for :class:`typing.Union`.
1082+
See :ref:`below <whatsnew314-typing-union>` for more details.
1083+
(Contributed by Jelle Zijlstra in :gh:`105499`.)
1084+
1085+
1086+
typing
1087+
------
1088+
1089+
.. _whatsnew314-typing-union:
1090+
1091+
* :class:`types.UnionType` and :class:`typing.Union` are now aliases for each other,
1092+
meaning that both old-style unions (created with ``Union[int, str]``) and new-style
1093+
unions (``int | str``) now create instances of the same runtime type. This unifies
1094+
the behavior between the two syntaxes, but leads to some differences in behavior that
1095+
may affect users who introspect types at runtime:
1096+
1097+
- Both syntaxes for creating a union now produce the same string representation in
1098+
``repr()``. For example, ``repr(Union[int, str])``
1099+
is now ``"int | str"`` instead of ``"typing.Union[int, str]"``.
1100+
- Unions created using the old syntax are no longer cached. Previously, running
1101+
``Union[int, str]`` multiple times would return the same object
1102+
(``Union[int, str] is Union[int, str]`` would be ``True``), but now it will
1103+
return two different objects. Users should use ``==`` to compare unions for equality, not
1104+
``is``. New-style unions have never been cached this way.
1105+
This change could increase memory usage for some programs that use a large number of
1106+
unions created by subscripting ``typing.Union``. However, several factors offset this cost:
1107+
unions used in annotations are no longer evaluated by default in Python 3.14
1108+
because of :pep:`649`; an instance of :class:`types.UnionType` is
1109+
itself much smaller than the object returned by ``Union[]`` was on prior Python
1110+
versions; and removing the cache also saves some space. It is therefore
1111+
unlikely that this change will cause a significant increase in memory usage for most
1112+
users.
1113+
- Previously, old-style unions were implemented using the private class
1114+
``typing._UnionGenericAlias``. This class is no longer needed for the implementation,
1115+
but it has been retained for backward compatibility, with removal scheduled for Python
1116+
3.17. Users should use documented introspection helpers like :func:`typing.get_origin`
1117+
and :func:`typing.get_args` instead of relying on private implementation details.
1118+
- It is now possible to use :class:`typing.Union` itself in :func:`isinstance` checks.
1119+
For example, ``isinstance(int | str, typing.Union)`` will return ``True``; previously
1120+
this raised :exc:`TypeError`.
1121+
- The ``__args__`` attribute of :class:`typing.Union` objects is no longer writable.
1122+
1123+
(Contributed by Jelle Zijlstra in :gh:`105499`.)
1124+
1125+
10621126
unicodedata
10631127
-----------
10641128

@@ -1592,6 +1656,11 @@ Changes in the Python API
15921656
This temporary change affects other threads.
15931657
(Contributed by Serhiy Storchaka in :gh:`69998`.)
15941658

1659+
* :class:`types.UnionType` is now an alias for :class:`typing.Union`,
1660+
causing changes in some behaviors.
1661+
See :ref:`above <whatsnew314-typing-union>` for more details.
1662+
(Contributed by Jelle Zijlstra in :gh:`105499`.)
1663+
15951664

15961665
Build changes
15971666
=============

Include/cpython/pystats.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
#define PYSTATS_MAX_UOP_ID 512
3333

34-
#define SPECIALIZATION_FAILURE_KINDS 37
34+
#define SPECIALIZATION_FAILURE_KINDS 44
3535

3636
/* Stats for determining who is calling PyEval_EvalFrame */
3737
#define EVAL_CALL_TOTAL 0

Include/internal/pycore_ceval.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ extern void _PyEval_DeactivateOpCache(void);
196196

197197
/* --- _Py_EnterRecursiveCall() ----------------------------------------- */
198198

199-
#if !_Py__has_builtin(__builtin_frame_address)
199+
#if !_Py__has_builtin(__builtin_frame_address) && !defined(_MSC_VER)
200200
static uintptr_t return_pointer_as_int(char* p) {
201201
return (uintptr_t)p;
202202
}
@@ -206,6 +206,8 @@ static inline uintptr_t
206206
_Py_get_machine_stack_pointer(void) {
207207
#if _Py__has_builtin(__builtin_frame_address)
208208
return (uintptr_t)__builtin_frame_address(0);
209+
#elif defined(_MSC_VER)
210+
return (uintptr_t)_AddressOfReturnAddress();
209211
#else
210212
char here;
211213
/* Avoid compiler warning about returning stack address */

Include/internal/pycore_opcode_metadata.h

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_stackref.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,6 @@
44
extern "C" {
55
#endif
66

7-
// Define this to get precise tracking of closed stackrefs.
8-
// This will use unbounded memory, as it can only grow.
9-
// Use this to track double closes in short-lived programs
10-
// #define Py_STACKREF_CLOSE_DEBUG 1
11-
127
#ifndef Py_BUILD_CORE
138
# error "this header requires Py_BUILD_CORE define"
149
#endif

Include/internal/pycore_structs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ typedef struct {
5757
// Define this to get precise tracking of stackrefs.
5858
// #define Py_STACKREF_DEBUG 1
5959

60+
// Define this to get precise tracking of closed stackrefs.
61+
// This will use unbounded memory, as it can only grow.
62+
// Use this to track double closes in short-lived programs
63+
// #define Py_STACKREF_CLOSE_DEBUG 1
64+
65+
6066
typedef union _PyStackRef {
6167
#if !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
6268
uint64_t index;

Lib/annotationlib.py

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import ast
44
import builtins
55
import enum
6-
import functools
76
import keyword
87
import sys
98
import types
@@ -33,18 +32,16 @@ class Format(enum.IntEnum):
3332
# preserved for compatibility with the old typing.ForwardRef class. The remaining
3433
# names are private.
3534
_SLOTS = (
36-
"__forward_evaluated__",
37-
"__forward_value__",
3835
"__forward_is_argument__",
3936
"__forward_is_class__",
4037
"__forward_module__",
4138
"__weakref__",
4239
"__arg__",
43-
"__ast_node__",
44-
"__code__",
4540
"__globals__",
46-
"__owner__",
41+
"__code__",
42+
"__ast_node__",
4743
"__cell__",
44+
"__owner__",
4845
"__stringifier_dict__",
4946
)
5047

@@ -77,14 +74,12 @@ def __init__(
7774
raise TypeError(f"Forward reference must be a string -- got {arg!r}")
7875

7976
self.__arg__ = arg
80-
self.__forward_evaluated__ = False
81-
self.__forward_value__ = None
8277
self.__forward_is_argument__ = is_argument
8378
self.__forward_is_class__ = is_class
8479
self.__forward_module__ = module
80+
self.__globals__ = None
8581
self.__code__ = None
8682
self.__ast_node__ = None
87-
self.__globals__ = None
8883
self.__cell__ = None
8984
self.__owner__ = owner
9085

@@ -96,17 +91,11 @@ def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None):
9691
9792
If the forward reference cannot be evaluated, raise an exception.
9893
"""
99-
if self.__forward_evaluated__:
100-
return self.__forward_value__
10194
if self.__cell__ is not None:
10295
try:
103-
value = self.__cell__.cell_contents
96+
return self.__cell__.cell_contents
10497
except ValueError:
10598
pass
106-
else:
107-
self.__forward_evaluated__ = True
108-
self.__forward_value__ = value
109-
return value
11099
if owner is None:
111100
owner = self.__owner__
112101

@@ -172,8 +161,6 @@ def evaluate(self, *, globals=None, locals=None, type_params=None, owner=None):
172161
else:
173162
code = self.__forward_code__
174163
value = eval(code, globals=globals, locals=locals)
175-
self.__forward_evaluated__ = True
176-
self.__forward_value__ = value
177164
return value
178165

179166
def _evaluate(self, globalns, localns, type_params=_sentinel, *, recursive_guard):
@@ -231,18 +218,30 @@ def __forward_code__(self):
231218
def __eq__(self, other):
232219
if not isinstance(other, ForwardRef):
233220
return NotImplemented
234-
if self.__forward_evaluated__ and other.__forward_evaluated__:
235-
return (
236-
self.__forward_arg__ == other.__forward_arg__
237-
and self.__forward_value__ == other.__forward_value__
238-
)
239221
return (
240222
self.__forward_arg__ == other.__forward_arg__
241223
and self.__forward_module__ == other.__forward_module__
224+
# Use "is" here because we use id() for this in __hash__
225+
# because dictionaries are not hashable.
226+
and self.__globals__ is other.__globals__
227+
and self.__forward_is_class__ == other.__forward_is_class__
228+
and self.__code__ == other.__code__
229+
and self.__ast_node__ == other.__ast_node__
230+
and self.__cell__ == other.__cell__
231+
and self.__owner__ == other.__owner__
242232
)
243233

244234
def __hash__(self):
245-
return hash((self.__forward_arg__, self.__forward_module__))
235+
return hash((
236+
self.__forward_arg__,
237+
self.__forward_module__,
238+
id(self.__globals__), # dictionaries are not hashable, so hash by identity
239+
self.__forward_is_class__,
240+
self.__code__,
241+
self.__ast_node__,
242+
self.__cell__,
243+
self.__owner__,
244+
))
246245

247246
def __or__(self, other):
248247
return types.UnionType[self, other]
@@ -251,11 +250,14 @@ def __ror__(self, other):
251250
return types.UnionType[other, self]
252251

253252
def __repr__(self):
254-
if self.__forward_module__ is None:
255-
module_repr = ""
256-
else:
257-
module_repr = f", module={self.__forward_module__!r}"
258-
return f"ForwardRef({self.__forward_arg__!r}{module_repr})"
253+
extra = []
254+
if self.__forward_module__ is not None:
255+
extra.append(f", module={self.__forward_module__!r}")
256+
if self.__forward_is_class__:
257+
extra.append(", is_class=True")
258+
if self.__owner__ is not None:
259+
extra.append(f", owner={self.__owner__!r}")
260+
return f"ForwardRef({self.__forward_arg__!r}{''.join(extra)})"
259261

260262

261263
class _Stringifier:
@@ -277,8 +279,6 @@ def __init__(
277279
# represent a single name).
278280
assert isinstance(node, (ast.AST, str))
279281
self.__arg__ = None
280-
self.__forward_evaluated__ = False
281-
self.__forward_value__ = None
282282
self.__forward_is_argument__ = False
283283
self.__forward_is_class__ = is_class
284284
self.__forward_module__ = None
@@ -765,9 +765,10 @@ def get_annotations(
765765
if hasattr(unwrap, "__wrapped__"):
766766
unwrap = unwrap.__wrapped__
767767
continue
768-
if isinstance(unwrap, functools.partial):
769-
unwrap = unwrap.func
770-
continue
768+
if functools := sys.modules.get("functools"):
769+
if isinstance(unwrap, functools.partial):
770+
unwrap = unwrap.func
771+
continue
771772
break
772773
if hasattr(unwrap, "__globals__"):
773774
obj_globals = unwrap.__globals__

0 commit comments

Comments
 (0)